rust icon indicating copy to clipboard operation
rust copied to clipboard

Tracking issue for box_syntax

Open aidanhs opened this issue 7 years ago • 26 comments

This is a tracking issue for the box_syntax feature, the special syntax for creating boxes, e.g. box 1usize (note: box patterns is separate and is tracked at at https://github.com/rust-lang/rust/issues/29641)

This was previously under the same tracking issues as placement (#22181/#27779) but I didn't want to remove it (yet) due to widespread usage in the compiler (Box::new is implemented in terms of it) and in the wider ecosystem (unlike <- etc which only had a handful of crates using it).

Things to decide:

  • how should box syntax be phased out?
    • how easy is it to remove box syntax as a language feature? Can we just describe it as "will never be stabilised in its current form as it's an internal implementation detail, may be stabilised under a new design of placement")?
      • if it were possible to remove it as a language feature, is it reasonable to do so without a replacement in the form of working placement?
      • if we want to keep box syntax internally, should we rename (but keep reserved) the keyword to something else to deliberately break users and indicate it shouldn't be used for placement?
    • is there any reason for users to use box syntax (apart from it looking nice) given Box::new is inline always, or does that not work?
  • if we decide to keep this, there are a number of unresolved questions in #22181 about the syntax behaviour

Note: when removing this, be careful not to remove the syntax entirely in order to not cause another case of https://github.com/rust-lang/rust/issues/50832

aidanhs avatar Apr 06 '18 14:04 aidanhs

Do I understand it correctly that the box syntax is dependent on placement-new (which has now been unapproved as an RFC)?

Personally I like the idea of having some syntactic sugar for creating something as common as a box (it does look nice). But obviously it would need to be built on something that works.

spacekookie avatar May 14 '18 10:05 spacekookie

@spacekookie yes, the big deal for box syntax is placement new.

steveklabnik avatar May 14 '18 15:05 steveklabnik

s there any reason for users to use box syntax (apart from it looking nice) given Box::new is inline always, or does that not work?

It doesn't optimize well, and even if it did, you would need some other way around it to avoid blowing up the stack in debug mode. https://github.com/rust-lang/rust/issues/41160

oyvindln avatar Jun 27 '18 10:06 oyvindln

is there any reason for users to use box syntax (apart from it looking nice) given Box::new is inline always, or does that not work?

So this is perhaps a bit of a weird usecase, but it is the only reason I have used box at all: I do a lot of no_std projects where the stack doesn't grow automatically. In these cases, using Box::new(MyLargeStruct) will overrun the limited amount of stack space by allocating MyLargeStruct on the stack and copying it to the allocated space. Allocating and initializing in place is critical.

The obvious alternative is to use the allocator directly and unsafely construct a Box from it, but that's annoying and ugly.

See example here: https://github.com/mark-i-m/os2/blob/2ab9d589a4463bd3133157b5c1a2f9f6735a3432/kernel/process/sched.rs#L76-L83

mark-i-m avatar Oct 25 '18 18:10 mark-i-m

In most cases people just want to simplify the Box::news everywhere. I really want a shorter version and box syntax sounds like a great idea. Is there a recent plan of stabilizing box syntax?

ice1000 avatar Mar 10 '19 21:03 ice1000

I think that there really isn't a convenient alternative for destructuring boxes without adding a lot of extra nesting. Personally, in very box-heavy code I would prefer that box destructuring be stabilized separately from box placement syntax. For instance, in this file I have 14 uses of the box destructuring syntax and only 1 of the box placement syntax. The code would become significantly less readable from the removal of box destructuring, whereas converting box <thing> with Box::new(thing) certainly doesn't bother me at all because the code is just as readable.

My thought then is that we should separate the tracking issue out for box destructuring so we can get that stabilized. The only reason we might not want to do this is if we want to create a general RFC for pattern matching items into Deref/DerefMut as described here. I don't intend to create said RFC at this time, but if someone else wants to do that then I think that is a viable alternative.

vadixidav avatar Apr 23 '19 20:04 vadixidav

This could possible alleviate my usecase: https://crates.io/crates/copyless

mark-i-m avatar May 01 '19 00:05 mark-i-m

@vadixidav box destructuring(/patterns) is already tracked at https://github.com/rust-lang/rust/issues/29641 (see mention of 'box patterns' in the issue description)

aidanhs avatar Nov 01 '19 12:11 aidanhs

I was looking at Box::pin when I noticed, that box_syntax is using the prefix syntax. But for the very reason (box x).into() is ugly to type and chain, the await operator(?) is using postfix syntax, So, maybe, for the same reasons, box should use postfix syntax as well?

(box x).into()

vs

x.box.into()

At least, that is what came to my mind. Sorry if this is the wrong ticket or off topic, I just want to see rust getting continuously better and better :smiley:

kellerkindt avatar Feb 27 '20 16:02 kellerkindt

x.box.into()

At least, that is what came to my mind. Sorry if this is the wrong ticket or off topic, I just want to see rust getting continuously better and better 😃

You could easily make a trait that adds a method and call it into_box or boxed:

x.boxed().into()

Implemented like:

trait Boxed {
    fn boxed(self) -> Box<Self>;
}

impl<T> Boxed for T {
    fn boxed(self) -> Box<Self> {
        Box::new(self)
    }
}

(adding type restrictions as needed)

tech6hutch avatar Apr 27 '20 14:04 tech6hutch

For my use case, I'm less concerned with syntactic sugar (Box::new is fine) but with blowing up the stack (in both debug and release mode) as @oyvindln and @mark-i-m suggested.

If I have a struct like:

const SIZE: usize = 4096*4096;
#[derive(Copy, Clone)]
#[repr(C, align(4096))]
pub struct Pages([u8; SIZE]);

impl Default for Pages {
    fn default() -> Self {
        Pages([0u8; SIZE])
    }
}

Creating a Box<Pages> without overflowing the stack is quite hard. Consider the following example implementations:

pub fn with_new() -> Box<Pages> {
    Box::new(Pages([0u8; SIZE]))
}

pub fn with_box_syntax() -> Box<Pages> {
    box Pages([0u8; SIZE])
}

pub fn with_default() -> Box<Pages> {
    Default::default()
}

pub fn with_raw_alloc() -> Box<Pages> {
    use std::alloc::{alloc_zeroed, Layout, handle_alloc_error};
    let layout = Layout::new::<Pages>();
    let ptr = unsafe { alloc_zeroed(layout) };
    if ptr.is_null() {
        handle_alloc_error(layout);
    }
    unsafe { Box::from_raw(ptr as *mut Pages) }
}

with_new blows the stack in debug and release, while with_box_syntax and with_default blow the stack only in debug mode. Godbolt Link

So even something like @mark-i-m's example isn't guaranteed to work. Should the box syntax in with_box_syntax be guaranteed to not blow the stack (even in debug mode)?

josephlr avatar Apr 30 '20 07:04 josephlr

@josephlr https://doc.rust-lang.org/std/boxed/struct.Box.html#method.new_zeroed_slice

fghzxm avatar Nov 12 '20 10:11 fghzxm

I'm sort of confused on the state of this feature. Will it ever be stabilized? What are the reasons it cannot be stabilized in its current form if so?

TanakaDev avatar Dec 01 '20 13:12 TanakaDev

I believe there is a bunch of design work left. In particular, it's not clear how adjacent features like a hypothetical "placement new" syntax would work, and how they would interact with box syntax.

mark-i-m avatar Dec 01 '20 18:12 mark-i-m

Here's a small, maybe naive, idea. Considering how #73394 was used to "pseudo-stablize" #64490, what if we had a macro that wrapped around box syntax? For instance:

#[allow_internal_unstable(box_syntax)]
macro_rules! boxed {
    ($item: expr) => {{
        box ($item)
    }}
}

notgull avatar Dec 24 '20 05:12 notgull

I'm working on a competing compiler to rustc, and I am particularily aiming to make Box not magic (the current impl is not even a lang item. It uses an unstable DerefMove trait and an internal attribute for moving out of boxes). I would support the proposed macro, certainly over specialized syntax (and I have written an implementation of it to that effect). I'm curious about the extent of the mandatory move-elision with box syntax, and thus the proposed macro. In particular, for expressions involving non-trivial control flow, would eliding the move and constructing the value in-place still be necessary?

chorman0773 avatar Dec 24 '20 13:12 chorman0773

If we can have typeof, we can simulate box syntax with the help of new_uninit (#63291):

macro_rules! boxed {
    ($e:expr) => {
        {
            let mut ws = Box::<typeof($e)>::new_uninit();
            let contents = $e;
            unsafe {
                // type inference is forbidden for raw ptrs, hence the need for typeof
                ws.as_mut_ptr().write(contents);
                ws.assume_init()
            }
        }
    }
}

See https://rust.godbolt.org/z/c5eE6T for a hacky demo; new_uninit generates the exact same assembly as box syntax (except on the error path), down to register selection.

Never mind, with maybe_uninit_extra it gets there without typeof (https://rust.godbolt.org/z/Kf1Kdh):

#![feature(maybe_uninit_extra, new_uninit)]

macro_rules! boxed {
    ($e:expr) => {
        {
            let mut ws = Box::new_uninit();
            ws.write($e);
            unsafe { ws.assume_init() }
        }
    }
}

fghzxm avatar Jan 01 '21 14:01 fghzxm

To implement it in user code, you'd need placement expressions or some other way to force the compiler into eliding the move and the intermediate value (where possible).

chorman0773 avatar Jan 01 '21 15:01 chorman0773

To implement it in user code, you'd need placement expressions or some other way to force the compiler into eliding the move and the intermediate value (where possible).

I doubt "forcing the compiler into eliding the move" is meaningful in the context of Rust or C or C++, since by the as-if rule the compiler is free to store garbage on the stack and perform unnecessary copies. In optimized builds on the other hand it seems sufficient to simply move the allocation in front of the evaluation of the stored value.

If the extra copy is strictly unacceptable even in unoptimized builds, my uneducated guess is that a compiler intrinsic would be more appropriate. (Note, however, that stack overflow could happen in unoptimized builds but not in optimized builds, if you have limited stack space, simply due to absent optimization; unoptimized code naturally uses more stack)

fghzxm avatar Jan 02 '21 01:01 fghzxm

One of the reasons for the box syntax I thought was to allow you to box large values that may cause a stack overflow if an intermediate value is not constructed in-place. This is a case where the difference between a move occurring and being elided can effect the behaviour of the code.

And yes, absent placement-expressions, you'd need either an intrinsic, or some other implementation-specific unstable way to implement this. In the implementation I mentioned, I used the latter. On Fri, Jan 1, 2021 at 20:30 fghzxm [email protected] wrote:

To implement it in user code, you'd need placement expressions or some other way to force the compiler into eliding the move and the intermediate value (where possible).

I doubt "forcing the compiler into eliding the move" is meaningful in the context of Rust or C or C++, since by the as-if rule the compiler is free to store garbage on the stack and perform unnecessary copies. In optimized builds on the other hand it seems sufficient to simply move the allocation in front of the evaluation of the stored value.

If the extra copy is strictly unacceptable even in unoptimized builds, my guess is that a compiler intrinsic would be more appropriate.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/49733#issuecomment-753414494, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGLD23CKJPQHSEMGMPDOF3SXZZMNANCNFSM4EZIVXJQ .

chorman0773 avatar Jan 02 '21 01:01 chorman0773

Now, in prospect of allocators api box can be implemented without a language feature; given deref patterns being merged one of prime reasons to have that syntax is gone.

The last reason to have this feature are these tricky optimizations.

And AFAIK Box being a language item is then only valuable for special casing of its layout in several contexts.

tema3210 avatar Aug 25 '21 08:08 tema3210

Note from backlog bonaza: This feature is perma-unstable. The Lang Team is not in any rush to remove this feature, but if a new stabilization-track feature appears that overlaps with box_syntax's functionality, we will start to look at removing box_syntax.

davidbarsky avatar Apr 27 '22 17:04 davidbarsky

Would it be worth renaming the feature to make its status clear? (something like box_syntax_deprecated). Or adding warnings?

glandium avatar Apr 27 '22 21:04 glandium

FTR, deref patterns (#87121) don't help with removal of box_syntax, only with removal of box_patterns (#29641).

box_syntax is still needed inside std facade crates, to implement Box::new, but also to implement vec![]. There are box syntax free alternatives for both, but in #87781 no alternative could be found to remove box syntax from those two places. See https://github.com/rust-lang/rust/pull/87781#issuecomment-894872367 for one of the perf run results. That being said, back when I filed the PR, both the latest and the minimum LLVM was older. Now it's 12 (#90175), so maybe the regressions are smaller now. Maybe it's time for a second attempt? If box syntax is only used in library crates, it might also be possible to give it a less prominent API, something that discourages nightly uses of it. If that is added though, it should be phased in gradually.

But yeah, going via the deprecation lint route might be useful because I suppose a lot of people are using box syntax. Unconditional warnings are equivalent to unconditional errors, because you can't turn them off. But a deprecation lint might work, idk. Given that it's a nightly feature, it could even start with deny by default.

est31 avatar May 22 '22 16:05 est31

I've opened #97293 to figure out a replacement syntax. In there, I introduce #[rustc_box] Box:new(x) which gets lowered to the internal representation of box x very early on during compilation. Then at a later point in time, box x can be removed from the early parts like AST. And then it's properly become an implementation detail.

Note that I have edited this comment, it didn't say at the start what I intended. Sorry for the trouble!

est31 avatar May 22 '22 21:05 est31

I've been wondering about #[rustc_box] Box:new(x): the issue with it is that it requires two feature gates to be enabled, both stmt_expr_attributes, and rustc_attrs. Furthermore, both are general features and enabling them exposes users to an unnecessarily large range of unstable features.

The alternative that I could think of would be a lang-item function like Box::new_in_place or a free function alloc::boxed::box_in_place with its own feature gate. What do people think?

est31 avatar Aug 05 '22 22:08 est31

The alternative that I could think of would be a lang-item function like Box::new_in_place or a free function alloc::boxed::box_in_place with its own feature gate. What do people think?

@est31 Yet another alternative would be to have a rustc_box!() macro or similar, but I don't know if that buys us much beyond a lang-item (beyond making it slightly clear that the macro intrinsic might be doing something beyond a pure function call).

estebank avatar Oct 03 '22 14:10 estebank

If it's going to be a public macro, it should be called boxed!() instead.

On Mon, 3 Oct 2022 at 10:46, Esteban Kuber @.***> wrote:

The alternative that I could think of would be a lang-item function like Box::new_in_place or a free function alloc::boxed::box_in_place with its own feature gate. What do people think?

@est31 https://github.com/est31 Yet another alternative would be to have a rustc_box!() macro or similar, but I don't know if that buys us much beyond a lang-item (beyond making it slightly clear that the macro intrinsic might be doing something beyond a pure function call).

— Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/49733#issuecomment-1265570729, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGLD25BQUMBRZWFA3QW7SDWBLWTTANCNFSM4EZIVXJQ . You are receiving this because you commented.Message ID: @.***>

chorman0773 avatar Oct 03 '22 15:10 chorman0773

@estebank Personally, I see the attribute on the function more as a guaranteed optimization. But admittedly you can see macros in the same way, as one of the things they provide is guaranteed inlining.

I've recently been drawn towards builtin#box(). There have been questions on how to pretty print format! invocations in the MCP for making format an AST token. builtin# syntax might also help with the implementation of a builtin offset_of macro, at least if that macro gets capabilities that can't be expressed soundly in current Rust.

So maybe the builtin# syntax could be used in multiple places. It might be the go-to syntax if something isn't quite a function, or a macro, but a custom syntax is too complicated for it. I've been thinking about doing an MCP for it. What do you think?

est31 avatar Oct 03 '22 16:10 est31

Apologies for the ping on this question for anyone else, but I haven't heard of builtin# before, where can I follow that development?

ShadowJonathan avatar Oct 04 '22 08:10 ShadowJonathan