Open nikomatsakis opened 4 years ago
With regard to the lint, is there any semantic difference between the two? Intuitively I would expect them to behave identically (static lifetimes).
They behave identically. The point of the lint is to have a single "idiomatic" form.
Naming: "inline const", "const block", or "anonymous const"?
The issue title calls it "const expressons" so maybe that should also be on the list.
I prefer "inline const" because it emphasizes that this is a totally separate body of code that is just written inline. That's much more like a closure than normal blocks.
Also, is anyone up for implementing this? (I can't even mentor this I am afraid, this affects surface-level syntax and MIR building which is way outside what I know.^^)
"const block" is clearly correct.
it matches "unsafe block" that way.
That false parallel with "unsafe block" is exactly why it is not correct. An unsafe block inherits scope, execution environment, everything from its parent block. An inline const does not.
I'm happy to mentor, although by no means am I an expert on all the parts of the compiler you'll need to touch. There's already an AnonConst
type in the AST and the HIR that should for the most part have the same rules as an inline const
(e.g. no outside generic parameters), which should make the lowering part of this pretty easy.
If no one volunteers in the next few weeks, I'll try to set aside a day or two to implement this. However, I'm mostly in maintenance/review mode as far as Rust is concerned.
Does const { }
differ from normal block expressions, however?
I think either "inline const" or "const expression" should be favored. Everyone's going to call it a constexpr anyways because of C++ user bleedover, and I don't think it's worth offering that much pushback this time. :^)
Hm... I need to fix my computer still. Guess I have a good reason today.
Does const { } differ from normal block expressions, however?
const {}
does not inherit the same scopes as block expressions do.
Everyone's going to call it a constexpr anyways because of C++
It can be a useful comparison when introducing C++ users to Rust's const
and const fn
, yet I have not seen most users conflate them. Additionally, C++ does not have constexpr { ... }
.
I don't think it's worth offering that much pushback this time.
There are no mentions of constexpr
or C++ on this issue before or the RFC. I find it unproductive to label it as pushback from that. The concerns about naming are mentioned in the RFC https://rust-lang.github.io/rfcs/2920-inline-const.html and the comments.
Alas, I was trying to make a rather more casual and conversational remark rather than something that I expected to be picked over with a fine-tooth comb and labeled "unproductive". Resolved.
However, when discussing consts, someone literally referred to a similar expression as a "constexpr", to me, today, when they were thinking about trying to write a macro that attempted to implement this feature.
I was not responding to the idea of using constexpr unproductive, and I mentioned that the comparison to it can still be useful. I specifically referred to describing current naming discussions as "pushback" to be unproductive. I believe we have had a misunderstanding, and I would be open to discussing this more in a direct message on discord. I do not think we should continue this here.
@ecstatic-morse I'd like to take this issue if it's not taken yet.
Go for it @spastorino!
I forgot to link this issue from the PR but the PR is now merged https://github.com/rust-lang/rust/pull/77124
I would like to write the documentation.
I would like to write the documentation.
@camelid please once you have something up cc me so I can review.
Note that right now the feature is named inline-const pretty much everywhere.
- [x] Implement the RFC (#77124 does most of it, modulo some bugs around range patterns that should be fixed in #78116)
As the above checkbox has been checked and both #77124 and #78116 have been merged, I assume this feature is fully implemented. Why is it still in the INCOMPLETE_FEATURES
list?
I think that's just an oversight, we should remove it from the incomplete features list.
I think that's just an oversight, we should remove it from the incomplete features list.
@oli-obk I'm willing to submit a PR, so I can use this in my code without warning.
Great! Feel free to r? @oli-obk
it
While working on promotion recently, here's something I wondered about... would it make sense for const {}
to use the promotion machinery? After all, that machinery already has all the support we need to let the const refer to generics of the surrounding context.
The check for the code in there should be the usual const check of course, not the implicit promotion check. But arguably the same is true for explicit promotion contexts, isn't it? const {}
would be as explicit as it gets for promotion contexts. ;)
The current explicit promotion contexts are rustc_args_required_const
and inline assembly const operands... do we still have time to take away promotion from both of them and require a const literal or inline const block?
@RalfJung I am not sure if i understood your question correctly
would it make sense for
const {}
to use the promotion machinery?
I personally don't know what exactly you are thinking of here. At least from where I am approaching this the way const blocks should deal with the generics of the surrounding context is quite different to promotion. Especially considering potentially failing operations like const { 64 / mem::size_of::<T>() }
which we probably want to allow but imo should require feature(const_evaluatable_checked)
. I can't really see that working well with promoteds as it happens during typeck while promotion happens afterwards.
The current explicit promotion contexts are rustc_args_required_const and inline assembly const operands... do we still have time to take away promotion from both of them and require a const literal or inline const block?
idk, it would sure be nice to do so. Don't know too much about either of those, I am however surprised that inline assembly const operands rely on promotion at all rn :laughing: would have expected them to use an AnonConst
instead.
I personally don't know what exactly you are thinking of here.
Basically, compile const { expr }
like expr
during MIR building (except with restrictions for which local variables are in scope), and then use this as a promotion site in transform/promote_consts.rs
.
const {}
is primarily a means to replace implicit promotion with something more explicit. Promotion already does everything we want const {}
to do, except that it is subject to some serious restrictions because it is implicit. So using promotion to implement const {}
seems like the easiest option.
Especially considering potentially failing operations like const { 64 / mem::size_of::
() } which we probably want to allow but imo should require feature(const_evaluatable_checked).
Uh, that's a big move compared to any prior discussion I am aware of.^^ This might smash all hopes I had of stabilizing this any time soon, which could be quite problematic for getting promotion cleaned up before it is too late.
I expected this would work just like promoteds and associated consts work today. No need to treat these likes const generics. (I know basically nothing about const_evaluatable_checked
, just that it is somehow related to ensuring that const generic parameters do not fail to evaluate.)
idk, it would sure be nice to do so. Don't know too much about either of those, I am however surprised that inline assembly const operands rely on promotion at all rn laughing would have expected them to use an AnonConst instead.
If AnonConst
is the same as inline const expressions, then asm!
predates that by quite a bit.
AnonConst
are used to represent constant expressions, so they are used for array lengths and associated constants.
moving this to zulip until I have a more solid understanding of our goals here. https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/topic/inline.20consts
Independent of the discussion around const_evaluatable_checked
, another way to phrase my point is: I think it is a bad idea to have both "explicit promotion" and inline consts. The two serve almost the same purpose, so having to come up with, maintain, and test two separate sets of rules is a lot of extra work for no gain that I can see.
I think we should aim at unifying the two. This means treating rustc_args_required_const
arguments (I assume some of the functions decorated with this are already stable?) as inline consts (and potentially, with an edition change, requiring the const { ... }
keyword if the argument isn't a literal or named const); for asm!
const
arguments there's still time to work out the details since this isn't stable yet.
Replying to @oli-obk's comment here
For rustc_args_required_const we can't do this because the invocation of the function would need to resolve the call before knowing that the argument is a const.
Name resolution happens before MIR construction, so we could still do it on that level... not sure how well that would work with the current implementaton of inline consts though.
Also, besides the point I made above about unifying explicit promotion and inline consts, here's another one: inline consts (with support for using generics) and implicit promotion seem to have a lot in common -- both are expressions that are syntactically "embedded" in their parent context and want to inherit at least some of its information (generics; possibly type inference information as well). So maybe it makes sense to implement them in similar ways?
It is this point together with the above that lead to my first message on the subject. The feedback I got helped tease the two points apart. :) I think the first one is much stronger than the second.
Name resolution happens before MIR construction, so we could still do it on that level... not sure how well that would work with the current implementaton of inline consts though.
Ah, good point, we could do this as an "expansion" in AST
-> HIR
lowering
both are expressions that are syntactically "embedded" in their parent context and want to inherit at least some of its information (generics; possibly type inference information as well). So maybe it makes sense to implement them in similar ways?
While not generally possible for anonymous constants, just for inline consts, that could be doable, but we'd need some sort of marker on the local that contains the final value of the inline const, so that "promotion" can reliably pick it up and uplift its creating MIR statements to a promoted MIR. Though considering that we had all the relevant information about what expressions are part of it before creating the MIR, picking it apart again later seems odd.
The oppsite way of unifying the systems would be to figure out what code is (implicitly) promotable on the HIR, but we explicitly moved away from such a system due to various problems with it.
An intermediate way could be to give inline consts (and possibly repeat expression lengths) their own wrapper type around AnonConst
, which does not give them their own DefId
, but THIR lowering will just create promoteds at MIR creation time instead of putting them into their own MIR body that may end up having cycles with their parent MIR body queries. That would still not unify the creation systems of promotion and inline consts, but it would treat them exactly the same once we are not processing HIR anymore.
Ah, good point, we could do this as an "expansion" in AST -> HIR lowering
Ah right, HIR is already name-resolved... so this should not be so bad.
The oppsite way of unifying the systems would be to figure out what code is (implicitly) promotable on the HIR, but we explicitly moved away from such a system due to various problems with it.
Yeah... I guess on the HIR, +
might still be a function call using the Add
trait. So that's probably not a good idea.
So, together with everything else you said, I tend to agree that it makes more sense to immediately build separate MIR bodies. That probably also makes it easier to use the normal const-expr checks here.
The key simplification that we can hopefully achieve, then, is to make transform/promote_consts.rs
only about (implicit) lifetime extension, and nothing else. "Promotion" and "lifetime extension" might mean the same thing again, as they once did. ;)
That would still not unify the creation systems of promotion and inline consts, but it would treat them exactly the same once we are not processing HIR anymore.
Promoteds are already very similar to regular consts on the MIR, so I don't think this would buy us much.
So, together with everything else you said, I tend to agree that it makes more sense to immediately build separate MIR bodies. That probably also makes it easier to use the normal const-expr checks here.
Promoteds are already very similar to regular consts on the MIR, so I don't think this would buy us much.
These two statements seem at odd to each other (since the first one may mean the "build promoteds at THIR time, but the second one says that doesn't gain us much), so just to be clear, so we are all on the same page: We don't change anything in the current system except that we generate AnonConst
in HIR lowering for rustc_args_required_const
, thus giving it its own DefId
and thus MIR
.
Sorry, to be more clear: in the second statement I meant the way they look once promotion is done messing up the MIR. Then promoteds are just an Operand::Const
, just like regular consts and inline const blocks, right?
yes.
We don't change anything in the current system except that we generate AnonConst in HIR lowering for rustc_args_required_const, thus giving it its own DefId and thus MIR.
Right, and likewise for asm!
const arguments.
This is assuming that equipping AnonConst
with support for generics isn't too hard (that's the thing that already works for promoteds).
Regarding rustc_args_required_const
, also see https://github.com/rust-lang/stdarch/issues/248. However, not promoting them at all breaks widely used crates, see https://github.com/rust-lang/rust/pull/80759. So if we want to get rid of explicit promotion, we do indeed have to use inline consts for rustc_args_required_const
-- or we use implicit promotion.
What are the blockers here? Naming is something that should be trivially resolved and doesn't really matter as to the language itself. The only other issue listed in the issue is a lint, which I personally don't think should preclude stabilization.
There are some ICEs listed here; AFAIK at least some of these are critical.
Regarding the discussion whether inline consts that use surrounding generics should be subject to const_evaluatable_checked
-style rules or not, here are some more arguments for why I think they should not:
&(T::ASSOC_CONST + 2)
is subject to implicit promotion, but if I wanted to be explicit and write const { &(T::ASSOC_CONST + 2) }
suddenly I'd need to add extra where
clauses. Note that I might forced to be explicit e.g. because I want to add a call to a const fn
in this expression.I know @estebank was shitposting on twitter, but I think the following actually has merit:
Postfix .const
for inline const expressions (not patterns though, as that probably has parsing issues). Doing this for expressions has various advantages:
I don't think I like it... we talk about const
-contexts for a reason, this is a very different "scope" to operate in. postfix makes this way too subtle. Also it doesn't support cases well where the thing inside the const block is not a method call (chain).
FWIW, some (crude) approximation of this feature can be implemented on stable with macros. ;)
let_chains
has the same "non-terminals" problem and was accepted regardless. Maybe the team will deal with it the next edition? IDK...
That said, what else is preventing the stabilization of inline_const
?
I believe I have fixed most problems with inline_consts. Inline const in pattern position still has some soundness issues; it's now in a separate feature gate, but I still hope that could be fixed before inline const in expr position is stablised.
Also, I have some feature extension to inline_const
planned and prototyped, but didn't have a lot time recently to finalize and polish it enough for a PR. For example, allow inline consts to reference generic parameters. In my prototype I can see that this is possible, and is very useful (e.g. you could use const { assert!() }
as static asserts).
I believe I have fixed most problems with inline_consts. Inline const in pattern position still has some soundness issues; it's now in a separate feature gate, but I still hope that could be fixed before inline const in expr position is stablised.
Also, I have some feature extension to
inline_const
planned and prototyped, but didn't have a lot time recently to finalize and polish it enough for a PR. For example, allow inline consts to reference generic parameters. In my prototype I can see that this is possible, and is very useful (e.g. you could useconst { assert!() }
as static asserts).
Thanks for the uddate, @nbdd0121!
From what I heard, the asm!
macro folks are eagerly awaiting inline const stabilization since const
arguments in asm!
are blocked on this. So it might make sense to not block inline consts in expressions on patterns or generic support -- those can be added later in a future-compatible way, can't they?
The asm!
's const is no longer same as inline const's, after inline const got type inference.
A bit of more detail: inline const used to be a thin wrapper around AnonConst implemented in rustc, which is also used by array lengths, repeat counts, etc. Asm's const also uses AnonConst. However this is changed because AnonConst
is type checked itself while for inline const type inference is wanted. Today, even though inline const impl still uses AnonConst
, it has additional handling and is typechecked in a similar way to closures. Asm's const and array lengths have DefKind::AnonConst
while inline const now is DefKind::InlineConst
.
Should asm!
be changed to use InlineConst
as well? It's not entirely clear to me what the differences are, but it seems that InlineConst
allows the use of generic parameters of the containing function.
I've just noticed that I'm assigned to this issue but I'm not working on it. I've done some initial work in the past but I'm not doing anything since a long while.
I guess @lcnr maybe be actively working on this or may know who is working on it.
Should
asm!
be changed to useInlineConst
as well? It's not entirely clear to me what the differences are, but it seems thatInlineConst
allows the use of generic parameters of the containing function.
yes, asm!
should probably also use InlineConst
instead of ordinary anon consts. @Amanieu please ping me on zulip if that's something you want to look into. Don't have too much time rn but want to really clean up the story wrt constants in rust.
Random idea I just had: what if ?
was special in inline consts? I don't think it ever makes sense for ?
inside a const{}
to return from the enclosing function -- it might not even be possible.
So we could potentially have const { "foo".parse()? }
mean the same as const { "foo".parse().unwrap() }
, with the error case giving a compilation error.
Usual disclaimer: this is an unbaked idea. It might be terrible, and is not a proposal.
I would expect this to be to case. Inline consts should behave similar to closures in that regard.
This is a tracking issue for the RFC "Inline
const
expressions and patterns" (rust-lang/rfcs#2920). Const expressions have been stabilized, but patterns have not The feature gate for the issue is#![feature(inline_const_pat)]
.This was originally a tracking issue for const blocks in both expression and pattern position. Inline const expressions have been stabilized in https://github.com/rust-lang/rust/pull/104087 while patterns are still unstable.
About tracking issues
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.
Steps
Unresolved Questions
&const { 4 }
vsconst { &4 }
?Implementation history
FnDef
type is disallowed in patterns: #114668