Question: margin-bottom: .625em; } <del> p { </del> <ins> p, .docblock > .warning { </ins> /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */

Among the suggested ideas, my favourite one remains the code block with "warning" as tag:
Yeah, but triple backticks is also for preformatted text without further markup, so you won't be able to e.g. use text formatting (or other code) in there
True... Finding the right syntax is much more complicated than expected...
That's the issue of using markdown, for all that it has a pretty nice baseline syntax it really was not designed for extensibility.
We can always use : it'd allow to have text formatting inside it.
I would use , it's not quite kosher to invent arbitrary HTML tags (though you can use Custom Elements, which is more heavyweight)
Sounds good to me. It's funny because we kinda came back to the . :rofl:
I just thought: we could use tag in the markdown and then transform it into in the generated HTML?
My main objection to all of these is that rustdoc shouldn't be adding custom markdown syntax that no one else supports. Any new syntax we use should look reasonable even if rendered with the default renderer.
Well, it remains valid markdown since it's HTML (even though it's custom tag). I'm completely fine with , but it might be simpler/better for users to simply write .
Coming back to this (because of ), it seems like the conversation kinda agreed on adding a CSS class directly and then it would look like this in the docs: . I'll send a PR in the next days and then we can open an FCP to see if everyone is ok with it (and if not, we'll have code to help the debate).
This indeed seems like a better solution than the HTML block. It also plays better with something said about not linking it too much too HTML. This will look like a proper warning or note, mostly independent of the HTML generation.
They also seemed to not be too favourable to anything that needs some extra parsing from rustdoc iuc. I'm fine with bot the github solution and the CSS class.
Maybe one more comment to make here. The note-taking app, which uses Markdown also has a similar syntax to create these blocks. It is called . It uses a syntax similar to the syntax that is used for doc intra-links currently, and it is fully compatible with them.


Question: margin-bottom: .625em; } <del> p { </del> <ins> p, .docblock > .warning { </ins> /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */

I am uncertain if this is any better than what suggested from the GitHub syntax, but because of the similarity to the doc intra-link syntax. I do feel like this might be interesting because it should go well with backwards compatibility. However, this may look worse than the GitHub syntax solution when not going through cargo doc.
I thought I'd already left this comment, but I'd really like to avoid adding more markdown syntax where possible. This feature works fine without special syntax, I don't think we need it. edit: copying over the reasoning from the :
Although, I might not agree fully, I can fully appreciate and understand the reasoning you left in the corresponding PR. Still, I do think it might be worth to standardize some kind of syntax for some kind of warning block. This does not actually require customized markdown. Alternatively, I can suggest adding the GitHub flavor with emojis to the . This may help in standardizing short form warnings and notes within documentation. We could for example propose to standardize: which should look like the following I do value people's opinion on this. Is this a possible compromise?
That's a valid alternative, I think the yellow color is pretty important though, and it's useful for themes to be able to make styling decisions about warnings so I do think that having some way of marking it is important. I just don't think the way of marking it should be some magic invocation as opposed to CSS classes. (And I think that the bar for a feature that is adding new semantics to the Rustdoc markdown parser is much, much higher than that of one stabilizing a CSS class)
100% agree. I will look at the Rust API guidelines issue and possibly an RFC to rust to track the adoption in GitHub and possibly eventually merge. Thank you :smile:
Beyond all this discussion of third-party tools, I'd really like to make sure that whatever new syntax and presentation we add to rustdoc can also be to [mdBook]. That way, when the standard library starts using callouts, we can make sure the same style also gets used in The Book. Mostly, The Book seems to just use blockquotes, often with headings like [this one about ownership]. mdBook doesn't do anything special with it: it's [just a block quote with a header].


Question: margin-bottom: .625em; } <del> p { </del> <ins> p, .docblock > .warning { </ins> /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */

[mdBook]: [this one about ownership]: [just a block quote with a header]:
This is a good point. Once we have something merged here, I'll send a PR there to have an equivalent.
CC
I'd be happy to add some CSS rules to mdbook if that is what you are proposing.
Yeah, it is.
FYI, using a is not semantic and Microsoft GitHub is setting a bad precedence. — CommonMark Spec v0.30, denotes a in Markdown and is rendered as such. — W3C HTML spec, Unless you are quoting a source, this is not the correct element to use. While I disagree with even using altogether, at least some tools out there are at least translating the blockquote in admonition context into a (div has no semantic meaning which is unfortunately the best HTML option for admonitions). It would be ill-advised to follow Microsoft GitHub’s Markdown syntax fork—especially with their disregard for semantics. Similarly using a block would also be breaking semantics as an admonition is not preformatted text. CommonMark does have that could be used if the solution was preferred. There’s also other reasonable options noted here that don’t break semantics.
I agree that block quotes for callouts are bad, and would like to work with mdBook to roll out a common syntax (probably ) that will replace the ad-hoc, unsemantic markup that they use right now.
Using is what we agreed upon in The debate now is on the look itself.
Unfortunately isn't great because markdown has a "boundary" when HTML tags are used. I wanted to add a warning indicating that users probably wanted another trait, but intra-doc links don't work (which is the correct behavior per markdown spec).
Works perfectly fine though (well, minus the UI bug I just discovered haha): ! I used this code: I'll send a PR to improve documentation.
Ah. Well I suppose the issue wasn't with intra-doc links, but with inline code blocks. What I tried was that but with bar`, and it failed.
Works too: !
I opened which adds extra explanation on how to use markdown inside html tags. That should solve your issue.
That note is exactly what I was just looking in to. I had it inline, given my familiarity with HTML. TIL that matters.


Question: margin-bottom: .625em; } <del> p { </del> <ins> p, .docblock > .warning { </ins> /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines. Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that. https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */

It doesn't hurt to have it and if it can helps users, well, why not doing it. :)


Question: if bcx.expr_is_lval(a) { let datum = unpack_datum!(bcx, trans_to_datum(bcx, a)); return match dest { <del> Ignore => datum.drop_val(bcx), </del> <ins> Ignore => drop_and_cancel_clean(datum, bcx), </ins> SaveIn(addr) => datum.move_to(bcx, INIT, addr) }; } else {

cc
cc
This was caused in the TOML refactor in Cargo, the fallback support to support malformed is likely broken.
Fixes are sent to Cargo, I'll send PRs to rust-lang/rust when they're merged
Submodules landed!


Question: <ins> //! To determine all the types that need to be private when looking at `Struct`, we //! used to invoke `predicates_of` to also look at types in `where` bounds. //! Unfortunately this also computes the inferred outlives bounds, which means for //! every field we check that if it is of type `&'a T` then `T: 'a` and if it is of //! struct type, we check that the struct satisfies its lifetime parameters by looking //! at its inferred outlives bounds. This means we end up with a `<Foo as Trait>::Assoc: 'a` //! in the outlives bounds of `Struct`. While this is trivially provable, privacy //! only sees `Foo` and `Trait` and determines that `Foo` is private and then errors. //! So now we invoke `explicit_predicates_of` to make sure we only care about user-written //! predicates. //@ check-pass mod baz { struct Foo; pub trait Trait { type Assoc; } impl Trait for Foo { type Assoc = (); } pub struct Bar<'a, T: Trait> { source: &'a T::Assoc, } pub struct Baz<'a> { mode: Bar<'a, Foo>, } } pub struct Struct<'a> { lexer: baz::Baz<'a>, } fn main() {} </ins>

https://crater-
searched nightlies: from nightly-2024-02-01 to nightly-2024-03-20 regressed nightly: nightly-2024-02-10 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.8</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: </detailsprobably
WG-prioritization assigning priority (). label -I-prioritize +P-high
MCVE


Question: Path { global: path.global, res: path.res, segments } } <ins> crate fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, ref path) => &path.segments, hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(), }; let mut s = String::new(); for (i, seg) in segments.iter().enumerate() { if i > 0 { s.push_str("::"); } if seg.ident.name != kw::PathRoot { s.push_str(&seg.ident.as_str()); } } s } </ins> crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut Vec<Item>) { let tcx = cx.tcx;

Rustdoc on [nightly documents][1] methods have signature of as while it only has to document it as . ! On [rustdoc stable][2], things are normal: ! : Version 1.49.0 ( 2020-12-29) [1]: [2]:
I'd like to investigate this :)
searched nightlies: from nightly-2020-12-29 to nightly-2021-01-22 regressed nightly: nightly-2021-01-09 searched commits: from to regressed commit: This regressed in , so this is probably caused by
This also affects Where will generate:
So a few observations: which displays mutability in patterns, causing to be displayed the mechanism for detecting and eliding works by , but as returns instead of , the comparison fails and displays the type.
I see two path to solving this bug: an option for to not display the mutability in patterns revert cc what do you think?
I would prefer to go for 1, there's no reason to have two completely different functions for pretty-printing.
Assigning and removing as in the prioritization working group.
(now a beta regression)


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

The following code fails on stable: When compiled in release mode, the assert is optimized out by LLVM even though it checks the exact same condition as the other assert, as can be verified by testing in debug mode. Note that it's theoretically possible for the stack to be aligned correctly such that the bug is suppressed, but that's not likely. The 256's can be replaced with larger alignments if that happens. The bug is caused by the impl of copying to the stack with the default alignment of 16. ()
and seem related
This is a soundness hole.
So... I guess the problem is that the that rustc emits for the unsized local doesn't use ? That's... kind of bad, because it doesn't look like even supports dynamically setting the alignment: If this is correct, the issue should be renamed to something like "unsized locals do not respect alignment".
A way to fix this would be to change the ABI for dynamic trait calls that take by value to always pass by address. When the trait object is created is where the trampoline function would be generated, so there is no additional cost for static calls.
This can also be fixed by not allowing dynamically-sized local variables with an alignment more than some maximum value and having all dynamically-sized locals use that maximum alignment unless optimizations can prove that a smaller alignment suffices.
Messing with the ABI wouldn't help with unsized locals in general (assuming is correct), since e.g. will also need an alloca with dynamic alignment in general. Given only a value, there is no static way to say anything about its alignment, so how would you distinguish whether calling a method on it is OK?
Can we call and then align the pointer for the local ourselves as a workaround until we come up with a good solution?
In clang there's a function which might or might not be useful in this case.
requires a constant alignment. Non constant alignments are . This matches semantics of LLVM instruction, so it's not helping, as alignment of value in is unknown at compile time when moving from .
I think that overallocating for unsized locals of unknown alignment may make sense. You can and then make use of to compute the offset. Sure, it increases stack requirements, but it seems acceptable for , and in most cases you end up with extra 7 bytes on stack or less. 256 alignment seems like an edge case where wasting 255 bytes doesn't matter.


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

if we keep setting as the default alignment we can skip the overallocating and offset dance for types with an alignment below that.
My idea was that rustc would prohibit coercing overaligned types to a dyn trait that takes by value. So:
The alignment is dynamically determined though, not sure if saving 15 bytes of stack space is worth the conditional branch?
Oh right. Yea that's probably not helpful.
Since , this would be backwards incompatible (at latest) once trait object upcasting is finally implemented: and trait objects would have to have the same alignment restriction (because you can upcast to ), but today you can freely create such objects with any alignment.
I was under the impression that calling didn't require dynamic , is that related to missing MIR optimizations, meaning we have unnecessary moves which end up duplicating the data on the stack using a dynamic ?
Correctness shouldn't rely on optimizations to fire...
Sure, I was just hoping we weren't relying on "unsized locals", only unsized parameters (i.e. ), for what we're exposing as stable. And I was hoping an optimization wouldn't need to be involved to get there. I wonder if it would even be possible to restrict "unsized locals" to the subset where we can handle it entirely through ABI, and not actually have dynamic s anywhere, since that would make me far more comfortable with 's implementation or even stabilization. Ironically, if that works, it's also the subset we could've implemented years earlier than we did, since most of the complexity, unknowns (and apparently bugs) are around dynamic s, not calling trait object methods on a dereference of .
That should be possible. I think everything that is necessary is performing the deref of the box as argument () instead of moving the box contents to a local first () That would also make it possible to call in cg_clif. Cranelift doesn't have alloca support, so the current MIR is untranslatable.
Hmm, but that's not trivially sound because it could lead to MIR calls where argument and return places overlap, which is UB. We'd potentially have to special-case it to derefs, but even then what's to stop someone from doing ? Maybe we could introduce a temporary for the return value instead, for these kinds of virtual calls? That should remove any potential overlap while allowing the call.


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

(Unrelatedly, I just realize we use a copying-out shim for vtable entries that take by value, even when would be passed as at the call ABI level. We would probably have to make computing the call ABI a query for this microopt to not be expensive)
That would result in being moved to a temp first before dropping and calling the boxed closure. If it wasn't moved first before the drop of , then borrowck would give an error. (I meant for this change to be done at mir construction level, not as an optimization pass, so borrowck should catch any soundness issues)
We can try it, I guess, but right now a lot relies on many redundant copies/moves being created by MIR building and may not catch edge cases like this. But also I don't think moving out of a moves the itself today. Me too, but I was going with the "correct by construction" approach of creating a redundant copy/move, just on the return side, instead of the argument side, of the call.
After revisiting on triage, removing nomination.
pre-triage: nominated for discussion in the next weekly meeting.
I just checked and, a function call roughly this (touched up for clarity): Some of my previous comments assumed the wasn't there and the call would use the destination directly, but given that there is a move, that simplifies my suggestion. <hr/For virtual by-value calls, we can skip one move when building the MIR, specifically the move out of the dereference of a for the argument. Given that we already move the result of the call, there should be no aliasing caused by this, but if we decide to change anything about how we build MIR calls, we'd need to be extra careful. We can also make this pattern be gated by a separate feature-gate (rather than ), and make sure the standard library only uses that feature-gate and not . This should solve any alignment issues might have, because we'd not be using dynamic unless the broader feature is used.
OK, if I understand what's being proposed here, it is to excerpt a subset of the "original unsized-locals plan" -- specifically, to permit locals with unsized types, but only in contexts where we can be guaranteed that we can point to the original storage and don't actually have to move any value (and hence we don't require variable size stack frames).


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

I'm in favor of this proposal, but I do think we want to be slightly careful to ensure we're not violating soundness. The main concern would be if there is some way to access or re-use the variable that was moved. I attempted to do so with this example () but it fails to compile: The error here arises because the closure would need to capture a to do anything, which prevents a later move. But maybe there is some kind of tricky example that can arise where we re-assign the value during argument evaluation?
compiles and does before , where is the result of .
Borrowck should prevent that, right?
borrowck may rely on MIR building creating extraneous copies, so we can't just assume it helps us. IMO we should limit it to functions taking unsized parameters and function calls passing such parameters or dereferences of es (any other pointer/reference doesn't support moving out of).
Well, I think is right that borrowck would catch violations of soundness in some strict sense, but I guess what I meant is that we might be breaking backwards compatibility.
The example that feels pretty relevant. If I understood your proposal, it would no longer compile. If I recall (thinking back), I had a plan to handle this by save the to an alternate location in the actual call, we pass ownership of and then do a shallow free of the This still seems like it would work, though I'm not sure how easy/elegant it is to formulate. In a sense we'd be rewriting to .
Isn't example dependent on doing a direct virtual call? My understanding is that you can't do this on stable with because the virtual call exists in one place, in libstd, and it doesn't do anything weird, so it would work with a restricted ruleset. EDIT: if it can work on stable, it's because calling 's will move the whole before the assignment is evaluated, which is fine, because the shallow drop is actually inside that , not its caller.
I agree that this code doesn't compile on stable today, but I'm not sure if that's the point. I guess you're saying it's ok for it to stop compiling? I think what I would like is that our overall behavior is as consistent as possible between Sized and Unsized parameters. I think we could actually do a more general change that doesn't special case Sized vs Unsized or anything else.


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

I think I am basically proposing the same thing as earlier, but with a twist: Today, when building the parameters, we introduce a temporary for each parameter. So if we had we might do: What I am saying is that we could say: If the argument is moved (not copied), and the "place" has outer derefs (e.g., we are passing where is some place), then we assign the place to a temporary and we make the argument to the function be : I don't believe this is observable to end-users, except potentially for when the destructor on runs. I believe that it accepts all the same code we used to accept. It also has nothing specific to or types at all. Does that make sense? I'm very curious to know if anyone sees any problems with it. (One obvious thing: if we ever add , its design would have to accommodate this requirement, but I think that's fine, in fact the complications around getting moves right for precisely this case are one of the reasons we've held off on thus far.)
Can you not reinitialize today? If you move the whole , it is indeed more conservative (and should let us skip unnecessary moves even in cases). But also, do you want to move the locals check from typeck to borrowck? I don't have an intuition for when your approach wouldn't create unsized MIR locals, from the perspective of typeck. However, this is only relevant if we want a stricter feature-gate. At the very least it seems like it would be easier to implement a change only to MIR building, and it should fix problems with for the time being, even without a separate feature-gate.
Hmm, that's a good point, I think do that. Sigh. I knew that seemed too easy. I remember we used to try and prevent that, but we never did it fully, and in the move to NLL we loosened those rules back up, so this compiles: Still, you could do my proposal for unsized types, though it would introduce an incongruity. UPDATE: Oh, wait, I remember now. For unsized types, reinitialization is not possible, because you can't do with an unsized rvalue (we wouldn't know how many bytes to move). So in fact there isn't really the same incongruity.


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

To be clear, I wasn't proposing changing anything apart from MIR building either, I don't think. IIUC, what you proposed is that would be compiled to: whereas under my proposal (limited to apply to unsized parameters, instead of moved parameters) would yield: I think I mis-stated the problem earlier. It's not so much that your proposal will introduce compilation errors, it's that I think it changes the semantics of code like this in what I see as a surprising way: I believe that this code would compile to the following under your proposal: which means that would be invoked with the new value of , right? Under my proposal, the code would still compile, but it would behave in what I see as the expected fashion. Specifically, it would generate:
Isn't there a way to "lock" that value through the borrow-checker, so further accesses would be disallowed, until the call returns? I'd rather be stricter here. <hr/My point was that if your proposal affects sized moves, you're breaking stable reinitialization. If it doesn't affect sized moves, I don't see how this applies anymore:
Let me just chime in here and say that this is a pain in Miri. Unsized locals are pretty different from sized locals, I had to introduce a bunch of special hacks to make them work. MIR locals even needed a new state just for them: Lazily allocating locals seems odd to me, but at least we can consistently do that for all locals. Elsewhere however to make this lazy initialization actually work out and catch mistakes where an unsized local is "overwritten" with a value of different size: This all feels really unprincipled, and it would be great if at some point we could come up with a coherent semantics for unsized locals in MIR. The worst part is that the size of a local is implicitly determined on the first write to it. It would be much better to be explicit about that, e.g. at time.
I don't think we have anything like this today, but presumably we could add such a thing, but I'm not quite sure what you have in mind. When does this prohibition on further accesses take effect?


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

In particular, the IR for under your proposal, if I understood, would be But the access to that (I believe) we wish to prevent takes place in the "evaluate remaining arguments" part of things. So I think we'd have to add some sort of "pseudo-move" that tells borrowck that should not be assignable from that point forward, right? Well, it depends on your perspective. It's true that, given the need to permit reinitialization, the check is now specific to unsized values -- i.e., MIR construction will be different depending on whether we know the type to be sized or not. That's unfortunate. However, I think we still maintain a degree of congruence, from an end-user perspective. In particular, users could not have reinitialized a anyhow. However, under my proposal, users can write things like and the code works as expected (versus either having an unexpected result or getting an error). In any case, it seems like we're sort of "narrowing in" on a proposal here: when we see a function call with a parameter that is , and the value being passed is a deref, we want to modify the IR to not introduce a temporary and instead pass that parameter directly from its location, either we do some sort of "no more assignments" lock, or we move the pointer and introduce a temporary (and then pass in a deref of that temporary) Does that sound right so far?
If it's easier to do the more flexible approach (by moving the pointer) than the "locking" approach (which is a bit like borrowing the pointer until all arguments are evaluated, then releasing the borrow and doing a move instead), I don't mind it, I was only arguing with the "uniformity" aspect. Something important here: your approach is stabilizable, while mine is meant more for that one in the standard library, and not much else.
Yes, I was kind of shooting for something that we could conceivably stabilize.
Removing nomination, this was already discussed in triage meeting and it's under control.
So should we maybe try out the approach I proposed and see how well it works?
yes, going to try that out.
For the record, the "new plan" is this.


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

When generating the arguments to a call, we ordinarily will make a temporary containing the value of each argument: But we will modify this such that, for arguments that meet the following conditions: The argument is moved (not copied), and its type is not known to be , and the "place" has outer derefs (e.g., we are passing P where P is some place), then we assign the place P (instead of ) to a temporary and we make the argument to the function be (instead of ). This means that given where , we used to generate but now we generate We are now moving the box itself, which is slightly different than the semantics for values known to be sized. In particular, it does not permit of the box after having moved its contents. But that is not supported for unsized types anyway. The change does permit modifications to the callee during argument evaluation and they will work in an analogous way to how they would work with sized values. So given something like: we will find that we (a) save the old value of to a temporary and then (b) overwrite while evaluating the arguments and then (c) invoke the with the old value of .
Update: There is also
is a perfectly normal operand, I don't know what you mean. It's the same kind of as we currently have in (on the RHS)
Oh sorry, somehow I thought operands could only refer directly to locals, not also dereference them. I think I am beginning to see the pieces here -- together with function argument handling, this entirely avoids having to dynamically determine the size of a local. I have to agree that is quite elegant, even though baking "pass-by-reference" into MIR semantics still seems like a heavy hammer to me. On the other hand this places a severe restriction on now unsized locals may be used, basically taking back large parts of Is there some long-term plan to bring that back (probably requires with dynamic alignment in LLVM)? Or should we amend the RFC or at least the tracking issue with the extra restrictions?
I would keep the more general feature but use two different feature-gates (if we can). And just like with a few other feature-gates () we could warn that (the general one) is incomplete and could lead to misaligned variables (or something along those lines).


Question: <ins> // run-pass #![feature(unsized_locals)] #![allow(dead_code)] #[repr(align(256))] struct A { v: u8, } impl A { fn f(&self) -> *const A { self } } fn f2(v: u8) -> Box<dyn FnOnce() -> *const A> { let a = A { v }; Box::new(move || a.f()) } fn main() { let addr = f2(0)(); assert_eq!(addr as usize % 256, 0, "addr: {:?}", addr); } </ins>

We could also add runtime asserts that the (dynamic) alignment is respected, once we know could never trigger them (e.g. from stable code).
I was wondering about that too. It seems like it'd be good to review the more general feature in any case. This is partly why I brought up the idea of wanting a guaranteed "no alloca" path -- this was something I remember us discussing long ago, and so in some sense I feel like the work we're doing here isn't just working around LLVM's lack of dynamic alignment support (though it is doing that, too) but also working to complete the feature as originally envisioned.
I two notes to the tracking issue so we don't overlook this in the future.
This is probably expected, but the fix failed to actually make sound:


Question: /// ``` /// /// ``` <del> /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] </del> <ins> /// #![feature(maybe_uninit_write_slice)] </ins> /// use std::mem::MaybeUninit; /// /// let mut vec = Vec::with_capacity(32);

The feature gate for the issue is . Tracking issues are used to record the overall progress of implementation. They are also uses as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label. [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide]) [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide]) [stabilization-guide]: https://rustc-dev- [doc-guide]: https://rustc-dev- Initial implementation ()
FWIW, here is a full-featured API provided by an external crate:
I wonder how this API relates to . It would be great if there was some safe API that wrapped around the use of to access a 's spare capacity, then updated based on the s .
Maybe we should follow that example and generalize to return ?
has implemented that, albeit as a private function. Sounds like a good idea to me.
Since exists now I think is redundant.
That's not actually true due to pointer invalidation, see and
I think it would be better to have this feature sooner rather than later. I'm starting to notice instances where people are doing things that are probably or definitely UB because they want this functionality. So I think this API falls squarely into the standard library's mission to provide pre-built and correct implementations of things that are subtle or difficult to get right.   (originally posted on but per Ralf's comments there, it is probably better to have this method for the above use cases)
I agree. I've been using this in my code and found it to be very useful. Even though is more general, I've never had a need for it and I feel that is useful on its own. The API being stabilized is: merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [x] [ ] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!


Question: /// ``` /// /// ``` <del> /// #![feature(maybe_uninit_write_slice, vec_spare_capacity)] </del> <ins> /// #![feature(maybe_uninit_write_slice)] </ins> /// use std::mem::MaybeUninit; /// /// let mut vec = Vec::with_capacity(32);

See for info about what commands tagged team members can give me.
while waiting for this feature to be stabilized, you should suggest that and use one of the convenience methods in:
:bell: This is now entering its final comment period, as per the . :bell:
Ralf mentioned this on , mostly rewording with my own opinion: I would prefer not to recommend that function, because even if you just want access to the spare region, under SB, these interfaces invalidate all pointers to the initialized region. I think that's too much of a footgun for codebases where people are already writing code that is rejected by Miri.
I wonder how many people do keep outstanding pointers to the initialized part while toying with the extra part 's implementation is already better than their 0-elems slices that get -ly converted into non-empty slices (and currently better than the stdlib one since it doesn't involve a 12-week delay for stable Rust). Anyhow, I've just released a new version () with a dedicated adapter: , for extra good measure.
To be clear, my concern is that the splitting interfaces are adding a landmine for the future. If I'm sending someone a PR I'll make sure it passes Miri at the time, but that's a hard condition to maintain. Especially because the two aforementioned codebases have dependencies that do int-ptr casts, an operation that Miri just doesn't understand at the moment, so it would be hard to advocate Miri in CI.
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.


Question: .find(|p| link.starts_with(**p)) { kind = Some(ValueNS); <ins> disambiguator = Some(&prefix[..prefix.len() - 1]); </ins> link.trim_start_matches(prefix) } else if link.ends_with("()") { kind = Some(ValueNS); <ins> disambiguator = Some("fn"); </ins> link.trim_end_matches("()") } else if link.starts_with("macro@") { kind = Some(MacroNS); <ins> disambiguator = Some("macro"); </ins> link.trim_start_matches("macro@") } else if link.starts_with("derive@") { kind = Some(MacroNS); <ins> disambiguator = Some("derive"); </ins> link.trim_start_matches("derive@") } else if link.ends_with('!') { kind = Some(MacroNS); <ins> disambiguator = Some("macro"); </ins> link.trim_end_matches('!') } else { &link[..]

I tried to document this code: I expected to see this happen: charstd::char` points to module page of char type points to char primitive page Instead, this happened: type points to module page of char :
I think the problem is that modules are in the type namespace, so is doing exactly what you are asking it for (the module, since it has higher priority). Since the built-in types scope is the lowest precedence, perhaps there needs to be a way to access it directly? Maybe something like or ? Or maybe should skip over modules, and keep looking deeper in the scope hierarchy? (cc )
Oh hmm I see what you mean. There are : Types, values, and macros. Since modules and types are in the same namespace, it uses the first one it finds. Maybe we could work around this by first resolving in an empty module (so it has nothing in scope), then only falling back to the current scope if we don't find something there?
I'm surprised this hasn't caused trouble before now. Does the root of never use the primitive?
I don't know how it works, but the resolver is smart enough to know that in a type context won't match a module (like , the won't match a module named "char". Maybe it has something to do with late-resolution  which has a to give it some context?
Adding that would be more of area ... I can give it a shot though.
It looks like doesn't even return a resolution and only records it for later use ... I'm going to go with the empty module workaround and we can fix it later if it turns out to cause issues.
The double resolve seems hacky. We could probably make type@ specifically not resolve to a module, perhaps? Also, note that modules are in all namespaces
How would that work? We could skip any module resolutions, but since modules have precedence over types I don't know how we'd get the primitive type to resolve afterwards.
Oh hold on we have a function already. We can look at directly.
This doesn't have anything to do with primitives, this has to do with glob imports vs non-glob imports. You can get the same issue with the following:
This also doesn't have to do with modules being "resolved first" or anything. Things that are non-glob in-scope are resolved first.


Question: .find(|p| link.starts_with(**p)) { kind = Some(ValueNS); <ins> disambiguator = Some(&prefix[..prefix.len() - 1]); </ins> link.trim_start_matches(prefix) } else if link.ends_with("()") { kind = Some(ValueNS); <ins> disambiguator = Some("fn"); </ins> link.trim_end_matches("()") } else if link.starts_with("macro@") { kind = Some(MacroNS); <ins> disambiguator = Some("macro"); </ins> link.trim_start_matches("macro@") } else if link.starts_with("derive@") { kind = Some(MacroNS); <ins> disambiguator = Some("derive"); </ins> link.trim_start_matches("derive@") } else if link.ends_with('!') { kind = Some(MacroNS); <ins> disambiguator = Some("macro"); </ins> link.trim_end_matches('!') } else { &link[..]

Actually, my bad: This does have to do with primitives in the sense that primitives are the only thing that still work when shadowed because of some special code in checking the primitive tables. I think when the string matches one of the primitive table entries for TypeNS lookups that were explicitly annotated with type@ and resolve to a module we can hack this in. In the future we might want to tighten up this code so that e.g. can't resolve to an enum, etc.
do you mind if I fix at the same time and resolve primitives even before modules? That seems like the less surprising behavior to me, the module can still be disambiguated with .
I'm a bit wary of doing this because having the char module in scope is not a common situation, and it will get weirder for people who have a different char module. I think we should resolve to whatever is in scope, but if is explicitly mentioned we can then force resolve to the primitive.


Question: use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; <ins> use crate::panic::AssertUnwindSafe; </ins> use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`]

https://crater-
I'll need to re-check later but this seems to bisect to somehow.
The UI test changes from that PR appear to back this up, as confusing as it is.
I'm not sure what's going on in that crate, if it built successfully by mistake, or how that PR interacts with it, so let's cc the PR author and reviewer for validation.
After , no longer implements and , because the context now contains a which doesn't implement those traits. So this is an issue with the standard library and not the crate being built. Repro: label -T-compiler +T-libs +S-has-mcve
WG-prioritization assigning priority (). Noticing this on which says that was meant to be confined on nightly. Unsure about the fallout of this regression but for now marking as critical for tracking purposes. label -I-prioritize +P-critical
Revert up:
Isn't not unwind safe?
That line was by your PR, so while what you say is technically true, talking about what "already happened" when we are traveling back and forth through time via version control may be more confusing than helpful.
That line was already there. I the line below it though. Original PR:
Apologies, I guess I can't count numbers today? Hm, so, the full error of the regression is this: Note that it's trying to make a pointer implement . And for what it's worth, stable does indeed document is . Thus unfortunately that line you pointed to, as opposed to the one I thought you were referring to, is effectively just documenting that all are , and that will include regardless of whether it is is . And confusingly, , unlike .
I have opened as an alternative.


Question: a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) { <ins> if let ty::ReEmpty = a { return; } </ins> let b = self.to_region_vid(b); let a = self.to_region_vid(a); self.add_outlives(b, a);

The following code () panics the compiler in stable 1.38.0, beta 1.39.0-beta.6, and nightly 2019-10-17: Removing the bound or filling it with makes it compile just fine. Error + backtrace:
cc
Hmm, I didn't know that adding a "syntactically trivial bound" could cause something like this. Is there some implicit bound that gets in cases like this?
minimized test case ():
The minimized test worked in previous stable Rust versions (at least in 1.33 under 2015 edition; though 2018 edition breaks there, which indicates this is with 99.999% certainty injected by MIR-borrowck...)
triage: P-high. Adding self to assignee list; removing I-nominated.


Question:
(cfg, _) => cfg.as_deref().cloned(), };  debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);   debug!( "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", item.name, item.cfg, parent, parent.and_then(|p| p.cfg.as_ref()), cfg );  Some(format!("
{}
", cfg?.render_long_html())) }

From Given the following Rust code with in-line annotations of the expected behaviour: On: does not seem to be able to generate feature requirement labels on types in private modules () that are publicly reexported (). The labels do not show up on the module overview nor page, but do propagate into items like functions for the given type. This renders the type without any feature requirement, neither in the module overview: ! Nor on the page: ! Note that the label is propagated onto (does not have an explicit ), at least understood that! It is supposed to render the feature requirement for like , both in the module overview above as on the page: ! It seems tricky to combine feature requirements on the and reexport. In the case above they are the same, but what if: The type is only publicly available when and are specified. Specifying is fine but makes the contents of unreachable through the current module, specifying only should result in a "module not found" error. CC
This also affects re-exports of entire crates. Unfortunately there the fix is not clear at all, since the "parent" crate is obviously not guaranteed to have the same features as the crate being re-exported.
Within the same crate as well; it's not about the features that are enabled (though it is relevant which are available) but about the combined restrictions that are placed on the definition and reexport. Simplest I can think of is a union of all features (but gets complicated quick when inversions and vs is used) and later perhaps simplifying based on feature relations (if implies there is no need to display as requirement, if either definition or reexport requires ). That's necessary to cleanly solve crate reexports, since you effectively want to ensure that the top-level crate feature implies feature and hide it. If the whole resolver is made smart enough (perhaps this already exists, I'm not too familiar with Cargo) it could even throw warnings when there exist feature combinations that would lead to a compiler error (ie. explicitly reexporting a symbol that is not available for a certain set of features).
I wonder if this is just an ordering issue of the passes: The private modules have disappeared by the point where we pass all the data down so we don't see them.


Question:
(cfg, _) => cfg.as_deref().cloned(), };  debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg);   debug!( "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", item.name, item.cfg, parent, parent.and_then(|p| p.cfg.as_ref()), cfg );  Some(format!("
{}
", cfg?.render_long_html())) }

Thanks for the suggestion - I moved before and (I've never built rustc/rustdoc from source, pleasantly surprised that only took just under 7min on a ThreadRipper :partyingface:) but it doesn't change anything to the generated output unfortunately :(
I observe this on public items as well: Does not show the badge at re-exports.
So from the following code: is stripped and then inlined. The stripped version contains all the correct information but the inlined one doesn't. Checking what's the best course here: either cloning the stripped item or propagating when inlining.


Question: msg: &str) -> DiagnosticBuilder<'a> { <del> let (level, src) = self.sets.get_lint_level(lint, self.cur); </del> <ins> let (level, src) = self.sets.get_lint_level(lint, self.cur, None); </ins> lint::struct_lint_level(self.sess, lint, level, src, span, msg) }

The following snippet works fine before but now it reports a warning: Steps to reproduce: clone rustup override set nightly-2017-08-11 cargo test Is this an expected behavior, or a bug? Thank you!
cc -- probably related to the new lint implementation
Yes this was caused by The bug is on . The check for whether is in scope is not checking the current set of attributes being linted. For example this does not warn: but this warns: (as you've noticed) The fix is to just take the local variable into account which has the up-to-that-point set of lint attributes collected.
I've opened a PR for this at


Question: <ins> // assembly-output: emit-asm // # zen3 previously exhibited odd vectorization // compile-flags: --crate-type=lib -Ctarget-cpu=znver3 -O // only-x86_64 // ignore-sgx use std::iter; // previously this produced a long chain of // 56:  vpextrb $6, %xmm0, %ecx // 57:  orb %cl, 22(%rsi) // 58:  vpextrb $7, %xmm0, %ecx // 59:  orb %cl, 23(%rsi) // [...] // CHECK-LABEL: zip_arrays: #[no_mangle] pub fn zip_arrays(mut a: [u8; 32], b: [u8; 32]) -> [u8; 32] { // CHECK-NOT: vpextrb // CHECK-NOT: orb %cl // CHECK: vorps iter::zip(&mut a, b).for_each(|(a, b)| *a |= b); // CHECK: retq a } </ins>

<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried this code: I expected to see this happen: same codegen or similar in performance Instead, this happened: array has way worse codegen <details<summaryAssembly</summary<p</p</details<!-- Provide the most recent version this worked on, for example: It most recently worked on: Rust 1.47 --It most recently worked on: 1.64 <!-- Provide the version you are using that has the regression. --65 till now. Regressed in nightly-2022-08-13, maybe LLVM 15 ?: : <!-- Did the compiler crash? If so, please provide a backtrace. --<!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details<!-- If you know when this regression occurred, please add a line like below, replacing with one of stable, beta, or nightly. -- modify labels: +regression-from-stable-to-stable -regression-untriaged <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
WG-prioritization assigning priority (). cc since this regressed in the LLVM 15 update label -I-prioritize +P-medium +T-compiler
I've seen llvm doing these strange 1-byte-at-a-time "vectorizations" in other places too. This might be a more general problem. In this case we can probably paper over it by implementing TrustedRandomAccess for the array iter.
Unfortunately, the array iterator is fundamentally worse right now, because indexing into it -- a fundamental part of being a by-value array iterator -- keeps it from SRoAing and thus doesn't optimize well. This is why when I was making better I ended up making to not have to use . Sadly, for the foreseeable future you're better off using the "weaker" slice iterator when you can.
My guess is that the SROA is missing something in the opaque pointer mode.


Question: <ins> // assembly-output: emit-asm // # zen3 previously exhibited odd vectorization // compile-flags: --crate-type=lib -Ctarget-cpu=znver3 -O // only-x86_64 // ignore-sgx use std::iter; // previously this produced a long chain of // 56:  vpextrb $6, %xmm0, %ecx // 57:  orb %cl, 22(%rsi) // 58:  vpextrb $7, %xmm0, %ecx // 59:  orb %cl, 23(%rsi) // [...] // CHECK-LABEL: zip_arrays: #[no_mangle] pub fn zip_arrays(mut a: [u8; 32], b: [u8; 32]) -> [u8; 32] { // CHECK-NOT: vpextrb // CHECK-NOT: orb %cl // CHECK: vorps iter::zip(&mut a, b).for_each(|(a, b)| *a |= b); // CHECK: retq a } </ins>

claim
It's not about vectorization. This optimization can be restored by using . But highly unrecommended. Upstream issue:
The upstream issue is closed. claim


Question: ) -> DefId { let body = tcx.mir_built(coroutine_def_id).borrow(); <ins> // If the typeck results are tainted, no need to make a by-ref body. if body.tainted_by_errors.is_some() { return coroutine_def_id.to_def_id(); } </ins> let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure)) = tcx.coroutine_kind(coroutine_def_id) else {

<!-- ICE: Rustc '-Zunstable-options --edition=2024 -ooutputfile -Zdump-mir-dir=dir' 'error: internal compiler error: impossible case reached', 'error: internal compiler error: impossible case reached' File: --auto-reduced (treereduce-rust): original: Version information ` Command: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongProgram output</strong</summary<p</p</details<!-- query stack: [coroutinebymovebodydefid] looking up the coroutine by-move body for [analysis] running analysis passes on this crate -- label +F-asyncclosure
As a step of reducing your code examples, could you please ensure that is necessary to reproduce this ICE before mentioning it in the issue? This ICE reproduces on edition 2021, which is the current stable edition.
My project fails on nightly also while using edition 2021.
Yep, a fix is already up and it's waiting for approval. I recommend downgrading to a slightly older nightly until this is fixed.


Question: <del> error: lifetime may not live long enough --> $DIR/issue-16922.rs:4:5 | LL | fn foo<T: Any>(value: &T) -> Box<dyn Any> { |                       - let's call the lifetime of this reference `'1` LL |     Box::new(value) as Box<dyn Any> |     ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` | help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound | LL | fn foo<T: Any>(value: &T) -> Box<dyn Any + '_> { |                                          ++++ error: aborting due to previous error </del>

we currently output The suggestion is incomplete and will not result in compilable code. Because the lifetime is coming from an argument of type , which is an unconstrained type parameter, itself will not have the needed lifetime and causes the obligation that we're trying to reverse by suggesting . The appropriate changes to the code would be either or , depending on whether appears in any other argument or not.
The suggested code .


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

I'm opening this up to serve as a tracking issue for enabling multiple codegen units in release mode by default. I've written up a before but the tl;dr; is that multiple codegen units enables us to run optimization/code generation in parallel, making use of all available computing resources often speeding up compilations by more than 2x. Historically this has not been done due to claims of a loss in performance, but the is intended to assuage such concerns. The most viable route forward seems to be to enable multiple CGUs and ThinLTO at the same time in release mode. Blocking issues: [x] Enable for libstd on all platforms - - - - Potential blockers/bugs: - -
cc
Presumably this will remain a stable compiler flag, but what else will change by default? Is the plan to make this only affect , or is the plan to also make this affect ? Do we want to make continue to use only a single CGU, to hedge against regressions for people who are already willing to trade off compiler time for runtime?
I would specifically propose that any opt level greater than 1 uses 16 codegen units and ThinLTO enabled by default for those 16 codegen units.
This is very interesting. One would think that ThinLTO-driven inlining should always have to do less work when our much more conservative pre-LLVM inlining. But that doesn't always seem to be the case, as in the . Most tests on the seem to profit though. Overall it's still not a clear picture to me.
was that comment meant for a different thread? I forget which one as well though, so I'll respond here! As a "quick benchmark" I compiled the regex test suite with 16 CGUs + ThinLTO and then toggled inlining in all CGUs on/off. Surprisingly inlining in all CGUs was 5s faster to compile, and additionally . In those timings is where inlining is only in one CGU, where is inlining in all CGUs. One benchmark, , got twice as slow!
Yeah, the comment was in response to (rust-doom) but since that was closed already, I put it here.


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

Regarding the compilation time difference between pre- and post-trans inlining, my hypothesis would be that sometimes pre-trans inlining will lead to more code being eliminated early on (as in the case of regex apparently) and sometimes it will have the opposite effect. I suspect that there's room for improvement by tuning which LLVM passes we run specifically for ThinLTO. Or do we do that already?
Heh it's true yeah, I'd imagine that there's always room for improvement in pass tuning in Rust :). Right now we perform (afaik) 0 customization of any pass manager in LLVM. All of the normal optimization passes, LTO optimization passes, and ThinLTO optimization passes are all the same as what's in LLVM itself.
I wanted to also take the time and tabulate all the results from the to make sure it's all visibile in one place. Note that all the timings below are comparing a release build to a release build with 16 CGUs and ThinLTO enabled Improvements All of the following compile times improved, sorted by most improved to least improved Regressions The following crates regressed in compile times, sorted from smallest regression to largest alexcrichton's attempt to reproduce the regressions Here I attempt to reproduce the regressions on my own machine with Unfortunately the only regression I was able to reproduce was the rust-belt regression. I'll be looking more into that.
Looking into , one interesting thing I've found is that the crate takes longer to compile with ThinLTO than it would otherwise. Looking at it appears that one codegen unit in this crate takes 99% of the time in LLVM. This codegen unit appears to be basically entirely dominated by . Almost all of the benefit of multiple codegen units is spreading out the work across all CPUs. Enabling ThinLTO to avoid losing any perf is fundamentally doing more work than what's already happening at , but we get speedups across the board in most cases. If we have one huge CGU, split it in two, and work on those in parallel then we've got 50% of the original time to run ThinLTO (in a perfect world). If ThinLTO takes more than 50% of the time then we'd have a build time regression, but that's almost never happening.


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

What's happening here is that we have one huge CGU, but when we split it up we still have one huge CGU. This means that the CGU which may take ~1% less time in LLVM only gives us a tiny sliver of a window to run ThinLTO passes. In the case of the crate this means that adding ThinLTO passes is an overall build time regression. So generalizing even further, I think that enabling ThinLTO and multiple CGUs by default is going to regress compile time performance in any crate where our paritioning implementation doesn't actually partition very well. In the case of we've got one huge function which hogs almost all the optimization time (I think) and the entire crate is basically dominated by that one function. I would be willing to conclude, however, that such a situation is likely quite rare. Almost all other crates in the ecosystem will benefit from the partitioning which should basically evenly split up a crate. maybe you have thoughts on this though?
Thank you so much for collecting and analyzing such a large amount of data, Your conclusions make sense to me. Unevenly distributed CGU sizes are also a problem for incremental compilation; e.g. the test sees 90% CGU re-use, yet compile time is almost the same as from-scratch. In the non-incremental case it might be an option to detect the problematic case right after partitioning and then switch to non-LTO mode? I'm not sure it's worth the extra complexity though. For cases where there is one big CGU but that CGU contains multiple functions, we should be able to rather easily redistribute functions to other CGUs. We'd only need a metric for the size of a . The number of MIR instructions might be a sufficient heuristic here. That would not help for but it might help for other crates. In conclusion, judging from the table above, I think we should make the default sooner rather than later. Open questions I see: What to do about ? I would have thought that it would always be a clear win to disable this when ThinLTO is enabled but that doesn't seem to be the case. Are there hardware configurations where this never is a win? E.g. should we default to traditional compilation if ?


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

FWIW, as the author of , which is a made-for-fun Piston game, I am more than happy to take a small compile regression when it seems like vast majority of crates get huge real-time improvements!
This seems like a good thing to have in our back pocket, but I'm also wary of trying to do this by default. For example the case is one where we may wish to disable ThinLTO but the one massive function could also critically rely on a function in a different CGU being inlined? That sort of case would be difficult for us to determine... In any case though I agree that we probably don't need the complexity just yet so I think we can defer this for later. Agreed! So far all the crate's I've seen the O(instructions) has been quite a good metric for "how long this takes in all LLVM-related passes", so counting MIR sounds reasonable to me as well. Again though I also feel like this is ok to have in our back pocket. I'd want dig more into the minor change example, I'm curious how awry the CGU distribution is there and whether a heuristic like this would help. Quite surprisingly I've yet to see any data that it's beneficial to compile time in release mode or doesn't hurt runtime. (all data is that it hurts both compile time and runtime performance!) That being said we're seeing such big wins from ThinLTO on big projects today that we can probably just save this as a possible optimization for the future. On the topic of inline functions I recently dug up again (later closed in favor of , but the latter may no longer be true today). I suspect that may "fix" quite a bit of the compile time issue here without coming at a loss of performance? In any case possibly lots of interesting things we could do there. Also a good question! I find this to be a difficult one, however, because the CGU number will affect the output artifact, which means that if we do this sort of probing it'll be beneficial for performance but come at the cost of more difficult deterministic builds. You'd have to specify the CGUs manually or build on the same-ish hardware to get a deterministic build I think? I think though that if you have 1 CPU then this is universaly a huge regression.


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

With one CPU we'd split the preexisting one huge CGU into N different ones, probably take roughly the same amount of time to opimize those, and then tack on ThinLTO and more optimization passes. My guess is that with one CPU we'd easily see 50% regressions. With 2+ CPUs however I'd probably expect to see benefits to compile time. Anything giving us twice the resources to churn the cgus more quickly should quickly start seeing wins in theory I think. For now though the deterministic builds wins me over in terms of leaving this as-is for all hardware configurations. That and I doubt anyone's compiling Rust on single-core machines nowadays!
One thing I think that's also worth pointing out is that up to this point we've mostly been measuring the runtime of an entire . That's actually, I believe, the absolute worst case scenario for where ThinLTO will provide benefit. Despite this, it's showing huge improvements for lots of projects! The benefit of ThinLTO and multiple CGUs is leveraging otherwise idle parallelism on the build machine. It's overall increasing the amount of work the compiler does. For a from scratch, though, you typically already have tons of crates compiling for the first half of the build in parallel. In that sense there's not actually any idle parallelism. Put another way, ThinLTO and multiple CGUs should only be beneficial for builds which dont have many crates compiling in parallel for long parts of the build. If a build is 100% parallel for the entire time then ThinLTO will likely regress compile time performance. Now you might realize, however, that one very common case where you're only building one crate is in an incremental build! Typically if you do an incremental build you're only building a handful of crates, often serially. In that sense I think that there's some massive wins of ThinLTO + multiple CGUs in incremental builds rather than entire crate builds. Although improving both is of course great as well :)
For deterministic builds you have to do some extra configuration anyway (e.g. path remapping) so I would not consider that a blocker. And I guess there are single core VMs around somewhere.


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

But all of this is such a niche case that I don't really care MIR-only RLIBs should put us into a pretty good spot regarding this, so I'm quite confident that we're on the right path overall.
That's really interesting. For incremental compilation enabled the situation might be different (because pre-trans inlining hurts re-use) but for the non-incremental case it sounds like it's pretty clear what to do.
Yeah, maybe we should revisit this at some point. Although I have to say the current solution of only and symbols and nothing in between is really nice and simple.
Hm yeah that's a good point about needing configuration anyway for deterministic builds. It now seems like a more plausible route to take! Also that's a very interesting apoint about incremental and inlining on our own end... Maybe we should dig more into those ThinLTO runtime regressions at some point! Also yeah I don't really want to change how we trans inline functions just yet, I do like the simplicity too :)
out of curiosity how did you figure out this and which function it was. I've wanted to look into why the webrender build is so slow and would welcome tips.
oh sure I'd love to explain! So I originally found as a problematic crate when compiling rust-belt as it just took awhile and I decided to dig deeper. I checked out the crate and ran: That drops in the current directory, and opening that up I see: ! The graph here isn't always the easiest to read, but we've clearly got two huge bars, both of which correspond to taking a huge amount of time for that one CGU (first is optimization, second is ThinLTO + codegen). Next I ran: and that command dumps a bunch of IR files into . Our interesting CGU is so I opened up (we sure do love our long filenames). Inside that file it was 70k lines and some poking around showed that one function was 66k lines of IR. I sort of forget now how at this point I went from that IR to determining there was a huge function in there though...
I don't know if this is the right place, but I think it should be considered whether the symbol issues reported in the Nightly section here: should be considered a blocker or not.


Question: run_pass_manager(cgcx, tm, llmod, config, true); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); timeline.record("thin-done"); <ins> // FIXME: this is a hack around a bug in LLVM right now. Discovered in // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen // error if there's an available_externally function in the LLVM module. // Typically we don't actually use these functions but ThinLTO makes // heavy use of them when inlining across modules. // // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this // function call (and its definition on the C++ side of things) // shouldn't be necessary eventually and we can safetly delete these few // lines. llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); timeline.record("no-ae"); </ins> Ok(mtrans) } }

The tl;dr is that certain versions of llvm, (i've seen this on 3.8, and whatever llvm rustc nightly uses) appends seemingly random garbage to the end of some names, e.g. we get: instead of: This knocks the debuginfo out of sync (it doesn't have the garbage appended). I've been able to repro this with clang3.8 for c++ files as well (haven't tested on other llvm), and switching to 5.0 seems to have fixed the issue. I don't know if its within reach, but perhaps we should attempt upgrading to llvm 5.0 before releasing this on stable? Note I understand this is for release mode, but i see this in debug mode on rustc nightly right now as well...


Question: self_ty: Ty<'tcx>, sig: ty::PolyGenSig<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { <del> debug_assert!(!self_ty.has_escaping_bound_vars()); </del> <ins> assert!(!self_ty.has_escaping_bound_vars()); </ins> let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty, sig.skip_binder().resume_ty]); sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty)) }

An there actually does panic for the test. I only moved this code between the files, the same comment and code pattern exists as well for normal closures: Originally posted by in
I couldn't repro this failure, maybe was doing instead of ? The ordering is important here.
I believe I did, yes.


Question: linker_flavor: LinkerFlavor::Ld, linker: Some("arm-none-eabi-ld".into()), asm_args: cvs!["-mthumb-interwork", "-march=armv4t", "-mlittle-endian",], <del> features: "+soft-float,+strict-align".into(), </del> <ins> // Force-enable 32-bit atomics, which allows the use of atomic load/store only. // The resulting atomics are ABI incompatible with atomics backed by libatomic. features: "+soft-float,+strict-align,+atomics-32".into(), </ins> main_needs_argc_argv: false, atomic_cas: false, has_thumb_interworking: true,

If you try to compile the crate with , it fails with the following error: This seems to be a regression from to , and my best guess for the change that caused it is this pull request: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Filed to fix this. FYI, a short-term workaround is to enable the target feature (). claim
Possibly related: This is seen cross-compiling from Linux to Windows. Giant 1MB linker error message related to atomicload, only in release mode, for target x8664-pc-windows-gnu. Failing in rustc 1.67.1 stable. Currently asking for help comments; will file an issue in a day or two if nobody reports a known fix.
Since native atomic ops exist in x86_64, I don't believe this and are related to your problem. Given that rend3 has enabled , your problem may be related to


Question: <ins> // check-pass #![feature(generic_const_exprs)] #![allow(incomplete_features)] #![deny(const_evaluatable_unchecked)] pub struct If<const CONDITION: bool>; pub trait True {} impl True for If<true> {} pub struct FixedI8<const FRAC: u32> { pub bits: i8, } impl<const FRAC_LHS: u32, const FRAC_RHS: u32> PartialEq<FixedI8<FRAC_RHS>> for FixedI8<FRAC_LHS> where If<{ FRAC_RHS <= 8 }>: True, { fn eq(&self, _rhs: &FixedI8<FRAC_RHS>) -> bool { unimplemented!() } } impl<const FRAC: u32> PartialEq<i8> for FixedI8<FRAC> { fn eq(&self, rhs: &i8) -> bool { let rhs_as_fixed = FixedI8::<0> { bits: *rhs }; PartialEq::eq(self, &rhs_as_fixed) } } fn main() {} </ins>

While checking whether the can be ported from typenum to nightly with , I hit this warning. I am not sure whether it's a false positive or whether the behavior I want is meant to be disallowed, but I think it should be allowed when enabling . I've reduced the code though I left some stuff to show why it would be useful. Originally posted by in
is emitted if we successfully evaluate an anonymous constant with generic const arguments. Anonymous constants currently inherit all of their parents generics, so for the constant has both and in scope, even though it only uses . This would mean that pretty much all anonymous constants would cause that lint, so we add a flag to their MIR to detect whether they actually use any generic parameter: uses so that flag is true. The problem is now that is concrete, so evaluating the constant is successful, but the constant refers to which is still generic. We should just disable the lint if is enabled :shrug: cc
Another similar. Short example failing on Nightly (thanks to ilyvion for narrowing it down): My use case: I know it's a semantic/voluntary constraint only, and it's quite recursive, but I believe it deserves its place. (Alternatives I tried are at )
this has been fixed, needs test to close


Question: #[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> {} <ins> #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] #[rustc_unsafe_specialization_marker] pub trait NonDrop {} // T: Copy as approximation for !Drop since get_unchecked does not advance self.alive // and thus we can't implement drop-handling #[unstable(issue = "none", feature = "std_internals")] impl<T: Copy> NonDrop for T {} #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] unsafe impl<T, const N: usize> TrustedRandomAccessNoCoerce for IntoIter<T, N> where T: NonDrop, { const MAY_HAVE_SIDE_EFFECT: bool = false; } </ins> #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl<T: Clone, const N: usize> Clone for IntoIter<T, N> { fn clone(&self) -> Self {


Question: #[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> {} <ins> #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] #[rustc_unsafe_specialization_marker] pub trait NonDrop {} // T: Copy as approximation for !Drop since get_unchecked does not advance self.alive // and thus we can't implement drop-handling #[unstable(issue = "none", feature = "std_internals")] impl<T: Copy> NonDrop for T {} #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] unsafe impl<T, const N: usize> TrustedRandomAccessNoCoerce for IntoIter<T, N> where T: NonDrop, { const MAY_HAVE_SIDE_EFFECT: bool = false; } </ins> #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl<T: Clone, const N: usize> Clone for IntoIter<T, N> { fn clone(&self) -> Self {


Question: let r = match datum.ty.sty { ty::ty_uniq(content_ty) => { <ins> // Make sure we have an lvalue datum here to get the // proper cleanups scheduled let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </ins> if type_is_sized(bcx.tcx(), content_ty) { <del> deref_owned_pointer(bcx, expr, datum, content_ty) </del> <ins> let ptr = load_ty(bcx, datum.val, datum.ty); DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) </ins> } else { // A fat pointer and a DST lvalue have the same representation // just different types. Since there is no temporary for `*e` // here (because it is unsized), we cannot emulate the sized // object code path for running drop glue and free. Instead, // we schedule cleanup for `e`, turning it into an lvalue. <del> let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </del> let datum = Datum::new(datum.val, content_ty, LvalueExpr); DatumBlock::new(bcx, datum)

Rust compiler (rustc 1.1.0-nightly ( 2015-05-14) (built 2015-05-14) fails to compile this source: With the error: I can reproduce this ICE also on
Reduced it a bit:
The broken IR:
Looking through it quickly, it seems like the order of the cleanups is messed up? If is then from the switch we go to the label which just jumps to where we memcpy the value for into the alloca and then continue to the label. This is where the problem is since it tries to call on the value returned from . But that's the thing, we wouldn't have called in that arm of the branch and hence shouldn't need to call free either.
Adding a block around suppresses the error. I feel like we had a similar problem not too long ago.
This is "caused" by the micro optimization for dereferencing owned pointer rvalues in . Since is the "trailing" expression for the arm its cleanup will be scheduled in the same scope as the match expression. cc
Either way, this is a dupe of I believe had an idea for a fix.
it's simple, we remove the micro-optimization and always return an datum from . The possibility of returning datum also caused issues with field accesses - see for that fix. We have a static type system and the / marker types are there for a reason. If there is no answer to "how is an rvalue dereference or field meaningful?", then the return type for both of those should be .


Question: let r = match datum.ty.sty { ty::ty_uniq(content_ty) => { <ins> // Make sure we have an lvalue datum here to get the // proper cleanups scheduled let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </ins> if type_is_sized(bcx.tcx(), content_ty) { <del> deref_owned_pointer(bcx, expr, datum, content_ty) </del> <ins> let ptr = load_ty(bcx, datum.val, datum.ty); DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) </ins> } else { // A fat pointer and a DST lvalue have the same representation // just different types. Since there is no temporary for `*e` // here (because it is unsized), we cannot emulate the sized // object code path for running drop glue and free. Instead, // we schedule cleanup for `e`, turning it into an lvalue. <del> let datum = unpack_datum!( bcx, datum.to_lvalue_datum(bcx, "deref", expr.id)); </del> let datum = Datum::new(datum.val, content_ty, LvalueExpr); DatumBlock::new(bcx, datum)

Original testcase by (reduced from ): The debug log reveals that a (shallow free) cleanup of the rvalue is scheduled in the scope of AST node , while the dereference expression itself has id : reveals that id refers to the entire function's block: This means the cleanup of a value valid only within a match arm is scheduled outside the arm. cc
Dupe of Leaving open for now but we can close either.
This is caused by a micro-optimization specific to dereferences of rvalues. Is this used oftenly enough to justify its existence in the first place? Even if it is, I guess we can disable it in problematic cases like this, where the temporary scope of the match arm's RHS is outside the arm.
I think we can probably remove it, in general removing special treatment of seems like a Good Thing
Triage: this still ICEs.
can you point me/us at the micro-optimization that you mentioned above, so that I (we) can try removing it ourselves and measure the impact?
I only vaguely recall this issue, but I'm pretty sure I was talking about .
triage: still ICEs.
Indeed, it doesn't even need : Does this at least continue to ICE even if LLVM is compiled without assertions? If not, we may want to up the priority on this ticket.


Question: /// # Examples /// /// ``` <del> /// format!("test"); /// format!("hello {}", "world!"); /// format!("x = {}, y = {y}", 10, y = 30); </del> <ins> /// format!("test");                             // => "test" /// format!("hello {}", "world!");               // => "hello world!" /// format!("x = {}, y = {val}", 10, val = 30);  // => "x = 10, y = 30" </ins> /// let (x, y) = (1, 2); <del> /// format!("{x} + {y} = 3"); </del> <ins> /// format!("{x} + {y} = 3");                    // => "1 + 2 = 3" </ins> /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")]

I was sent here from the . Please advise if I am posting in the wrong area. format! macro documentation could be improved by changing one of the examples to be more obvious to new users. Change the example to Additionally, assert! could be used to demonstrate final string values: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Something like ? claim
I think the use of "val" certainly makes it more obvious than my suggestion to use "z". Your suggestion would also do it in one line... I guess the question is whether you would prefer to make it more obvious (readable) by splitting it into two lines. This is my first contribution to Rust in any form, so I'm happy to go with your suggestion.
Thanks all!


Question: _ => unreachable!(), }; <del> write_or_print(&out, ofile); </del> <ins> write_or_print(&out, ofile, sess); </ins> } pub fn print_after_hir_lowering<'tcx>(

If you , you get a normal error like but if you try to at the same time: rust will panic which is a bit weird. Mayne that panic should be demoted to a regular error? <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
To reproduce: `
claim


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // RFC 736 (and Issue 21407): functional struct update should respect privacy. // The `foo` module attempts to maintains an invariant that each `S` // has a unique `u64` id. use self::foo::S; mod foo { use std::cell::{UnsafeCell}; static mut count : UnsafeCell<u64> = UnsafeCell { value: 1 }; pub struct S { pub a: u8, pub b: String, secret_uid: u64 } pub fn make_secrets(a: u8, b: String) -> S { let val = unsafe { let p = count.get(); let val = *p; *p = val + 1; val }; println!("creating {}, uid {}", b, val); S { a: a, b: b, secret_uid: val } } impl Drop for S { fn drop(&mut self) { println!("dropping {}, uid {}", self.b, self.secret_uid); } } } fn main() { let s_1 = foo::make_secrets(3, format!("ess one")); let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... //~^ ERROR field `secret_uid` of struct `foo::S` is private println!("main forged an S named: {}", s_2.b); // at end of scope, ... both s_1 *and* s_2 get dropped.  Boom! } </ins>

Tracking issue for: rust-lang/rfcs Functional record update does not mark the source as consumed, even if does not implement Copy. This means that e.g. the source's destructor will eventually be run, so the following code prints :
Note that, as discovered, you can also explicitly consume , also allowing code like:
:bomb:
Is legal, even if all members are private? It's a bit surprising
It is. There's the potentially-useful case where some of the members are private, and FRU allows you to update the public ones. Of course it should consume the source even then.
P-backcompat-lang, 1.0 beta
In my opinion the problem here is the privacy violation. The idea is that is expanded to and so forth. In this case, that does not trigger a move, because the types of those fields are . I think that part is acting as expected. What is surprising is that one can do this from outside the module.
That was (but my favored solution isn't minor).
When I initially saw this problem, I did not see it as a privacy violation, for the (obvious?) reason that since no private members of had been mentioned in the code , the client module is not breaking any abstraction introduced by privacy. I think this line of thinking is analogous to outline of the useful case where FRU allows one to update the public members of a type and silently move the private ones as well, via . This is useful for the case where the author of wants to reserve the right to add new private members to while still allowing for clients to use the convenient FRU syntax. I originally was going to try to explain my point of view via analogy with something from hygienic macros: If I were to define (and export) a macro within 's defining module: then it should, in principle, not be a privacy violation to use from client code outside 's defining module. (That is, in principle, hygiene should allow macros to expose controlled access to private internals.) Then I realized the crucial problem with that kind of analogy: is something that the designer of has to choose to put into the module; i.e., it is opt-in. But currently in Rust, FRU is not opt-in; its not even something you can opt out of.


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // RFC 736 (and Issue 21407): functional struct update should respect privacy. // The `foo` module attempts to maintains an invariant that each `S` // has a unique `u64` id. use self::foo::S; mod foo { use std::cell::{UnsafeCell}; static mut count : UnsafeCell<u64> = UnsafeCell { value: 1 }; pub struct S { pub a: u8, pub b: String, secret_uid: u64 } pub fn make_secrets(a: u8, b: String) -> S { let val = unsafe { let p = count.get(); let val = *p; *p = val + 1; val }; println!("creating {}, uid {}", b, val); S { a: a, b: b, secret_uid: val } } impl Drop for S { fn drop(&mut self) { println!("dropping {}, uid {}", self.b, self.secret_uid); } } } fn main() { let s_1 = foo::make_secrets(3, format!("ess one")); let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... //~^ ERROR field `secret_uid` of struct `foo::S` is private println!("main forged an S named: {}", s_2.b); // at end of scope, ... both s_1 *and* s_2 get dropped.  Boom! } </ins>

Any struct automatically is given support for it, whether it be appropriate for that struct or not. And that seems wrong, at least given examples like the one provided here in . This latter point, that providing support for FRU should not be something that is silently attached to a type like , seems somewhat in line with the thinking given in . But I am also pretty sure we cannot adopt anything of the scope given in that draft. (Still, maybe there's a hint of solution embedded within, in the sense of having some way of marking a type as data vs abstract; see final two bullet points below.) So, as I see it, we can either: out a way to support the point-of-view outlined here in the description for , in that should somehow be treated as consuming . Such a change seems like it would be quite subtle, since we would need to have some way of expressing that code like is sound, even if the types of the fields and are consumed by their respective and methods. (In other words, need to consume , yet somehow not completely consume it. Maybe there's a way to think of it as consuming "whatever is left of the type", I do not know.) Advantages: This would address the bug as described by the issue author, and would not break any code that was not already broken. Drawbacks: It is not clear what the semantics are for this form (in other words: the phrase "would not break any code" above is only true if you first accept that this hypothesized semantics exists). Also, there may not be time to do this properly for 1.0. , try to adopt a data/abstract-type distinction along the lines of the one in draft RFC. (I'm not going to write Advantages/Drawbacks for this; I think we clearly do not have time to do it that way for 1.0) , change FRU into a non-hygienic expansion into the record construction form with all fields listed, in the sense that is synonymous with and subject to privacy checks. Advantages: Seems really simple to implement, and is consistent with at least one core team member's mental model of FRU. Drawbacks: If we did this, then code like the example above would break.


Question: <ins> // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // RFC 736 (and Issue 21407): functional struct update should respect privacy. // The `foo` module attempts to maintains an invariant that each `S` // has a unique `u64` id. use self::foo::S; mod foo { use std::cell::{UnsafeCell}; static mut count : UnsafeCell<u64> = UnsafeCell { value: 1 }; pub struct S { pub a: u8, pub b: String, secret_uid: u64 } pub fn make_secrets(a: u8, b: String) -> S { let val = unsafe { let p = count.get(); let val = *p; *p = val + 1; val }; println!("creating {}, uid {}", b, val); S { a: a, b: b, secret_uid: val } } impl Drop for S { fn drop(&mut self) { println!("dropping {}, uid {}", self.b, self.secret_uid); } } } fn main() { let s_1 = foo::make_secrets(3, format!("ess one")); let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... //~^ ERROR field `secret_uid` of struct `foo::S` is private println!("main forged an S named: {}", s_2.b); // at end of scope, ... both s_1 *and* s_2 get dropped.  Boom! } </ins>

, let FRU keep its current hygienic (aka "privacy violating") semantics, but also make FRU something one must opt-in to support on a type. E.g. make a builtin trait that a struct must implement in order to be usable with FRU. (Or maybe its an attribute you attach to the item.) Advantages: Also seems really simple to implement (in the language). Accommodates users of FRU for the example. Drawbacks: If we did this, it would impose a burden on all code today that makes use of FRU, since they would have to start implementing . Thus, not simple to implement for the libraries and the overall ecosystem. , a hybrid between the previous two bullet-points: By default, treat FRU as non-hygienic expansion, but add a builtin trait that one can opt-into to get the hygienic (aka "privacy violating") semantics. As a bonus, perhaps implementing on a struct would also imply implementing on that struct, which seems like it would basically give us something very similar to the semantics desired by (Or, again, maybe its an attribute you attach to the item. This detail does not particularly matter to me.) Advantage: While this is obviously more complicated than the previous two bullet-points, it has the advantage that it has a staged landing strategy: We could just_ implement the change to FRU to be non-hygienic expansion for 1.0 beta. We could add at an arbitrary point in the future; it would not have to be in the 1.0 release. Drawback: People have claimed that some of my past proposals were a bit ... baroque. I imagine this one is no exception. :) (In other words, this semantics is a little complicated to explain to a newcomer.)
I don't want to make a big fuss about it, but I do want to note, again, that our "deadlines" are entirely self-imposed, and that under these circumstances "we don't have time" is slightly odd, because we can make time, any time we want. The blanket refusal to even contemplate doing that - even in light of new information - and to weigh the tradeoffs on their own merits is something I still don't really understand.