Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closures cannot be constants #50623

Closed

Conversation

leoyvens
Copy link
Contributor

During type collection, error if a closures is found in constant position, catching that before they go causing ICEs.

Fixes #50600.
Fixes #48838.

During type collection, error if a closures is found in constant
position, catching that before they go causing ICEs.

Fixes rust-lang#50600.
Fixes rust-lang#48838.
@rust-highfive
Copy link
Collaborator

r? @petrochenkov

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 10, 2018
if gen.is_some() {
let hir_id = tcx.hir.node_to_hir_id(node_id);
return tcx.typeck_tables_of(def_id).node_id_to_type(hir_id);
}

if !tcx.has_typeck_tables(def_id) {
span_err!(tcx.sess, span, E0912, "closures cannot be constants");
Copy link
Member

@cramertj cramertj May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: closures can be constants:

const FOO: fn() = || println!("Gah!");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, what about expected numerical constant, found closure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds much better to me, thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this check at all? I'd assume if we left it out, we'd get the real typeck error.

@petrochenkov
Copy link
Contributor

r? @oli-obk

@leoyvens
Copy link
Contributor Author

I changed the error message.

@oli-obk We need the check because these closures have no outer environment and therefore no typeck tables, so type check ICEs in tcx.typeck_tables_of right here.

@oli-obk
Copy link
Contributor

oli-obk commented May 11, 2018

@leodasvacas I understand that we need the check in the const interpreter, but typeck can already correctly process type mismatches in e.g. statics.

static U: usize = |x: u8| {};
error[E0308]: mismatched types
 --> src/main.rs:1:19
  |
1 | static U: usize = |x: u8| {};
  |                   ^^^^^^^^^^ expected usize, found closure
  |
  = note: expected type `usize`
             found type `[closure@src/main.rs:1:19: 1:29]`

so... we should probably figure out how to make the parent of the closure be the constant?

@oli-obk
Copy link
Contributor

oli-obk commented May 11, 2018

The PR as it stands probably errors on

struct Foo ([u8; (|x: u8| { }, 9).1]);
enum Functions {
    Square = (|x:i32| { }, 42).1,
}

And I think that worked at some point in time.

@leoyvens
Copy link
Contributor Author

leoyvens commented May 11, 2018

Good examples, this PR indeed errors on them but the current stable will ICE on them, don't know if it worked on older versions.

we should probably figure out how to make the parent of the closure be the constant?

That could work, but what do you mean by "the constant"? I tried attaching closure and other sub-exprs to outermost expr of a Repeat expr but it didn't work, don't know why I was misunderstanding what at Repeat is, will try some more. Also I noticed that struct Foo ([u8; { |x: u8| { } } ]); (with the block around the closure) will currently give an ordinary typeck error rather than ICE (don't know why either) but with this patch will give the new error.

@leoyvens
Copy link
Contributor Author

While playing with this I found another issue #50688, not sure if related.

@oli-obk
Copy link
Contributor

oli-obk commented May 12, 2018

Sounds very much related (taking the wrong surrounding scope (main instead of the constant)).

@oli-obk
Copy link
Contributor

oli-obk commented May 12, 2018

don't know if it worked on older versions.

@leoyvens
Copy link
Contributor Author

In the previous commit I made some progress fiddling with the definition tree, now the struct example works. I opened a bug to track the enum discriminant example, there is probably a better structural fix to all these issues but this PR already makes good progress.

@@ -268,34 +269,41 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
let parent_def = self.parent_def;

match expr.node {
self.parent_def = match expr.node {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you store this in a temporary variable and assign the self.parent_def = temp.or(self.parent_def) below? I think it would make the steps involved here a bit clearer

struct Foo ([u8; (|x: u8| { }, 9).1]);

// FIXME(#50689) We'd like the below to also work,
// but due to how DefCollector handles discr_expr differently it doesn't right now.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also add a this fixme the the location where discr_expr is handled?

@leoyvens
Copy link
Contributor Author

Thank you for the review, I've addressed the comments.

if gen.is_some() {
let hir_id = tcx.hir.node_to_hir_id(node_id);
return tcx.typeck_tables_of(def_id).node_id_to_type(hir_id);
}

if !tcx.has_typeck_tables(def_id) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this check? I thought the DefIds were setup correctly now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The def ids in discriminants will still have the enum as parent (so will fail here), for structs a case like struct Foo([u8; |x: u8| { }]) will also fail because the closure itself is the outer expression so it will have no parent to attach. So yhea without the check this would still ICE in some cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh... I thought the changes that made (|x: u8| {}, 42).1 work also got rid of the ICE.

@oli-obk
Copy link
Contributor

oli-obk commented May 15, 2018

@bors r+

@bors
Copy link
Contributor

bors commented May 15, 2018

📌 Commit 0356813 has been approved by oli-obk

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 15, 2018
@nikomatsakis
Copy link
Contributor

@bors r-

Wait, hold up. We're supposed to — at least in some cases — permit closures as constants.

cc @eddyb

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels May 17, 2018
@kennytm kennytm added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels May 17, 2018
@leoyvens
Copy link
Contributor Author

Closing in favour of #50851 which is a real fix.

@leoyvens leoyvens closed this May 18, 2018
bors added a commit that referenced this pull request May 20, 2018
rustc: introduce {ast,hir}::AnonConst to consolidate so-called "embedded constants".

Previously, constants in array lengths and enum variant discriminants were "merely an expression", and had no separate ID for, e.g. type-checking or const-eval, instead reusing the expression's.

That complicated code working with bodies, because such constants were the only special case where the "owner" of the body wasn't the HIR parent, but rather the same node as the body itself.
Also, if the body happened to be a closure, we had no way to allocate a `DefId` for both the constant *and* the closure, leading to *several* bugs (mostly ICEs where type errors were expected).

This PR rectifies the situation by adding another (`{ast,hir}::AnonConst`) node around every such constant. Also, const generics are expected to rely on the new `AnonConst` nodes, as well (cc @varkor).
* fixes #48838
* fixes #50600
* fixes #50688
* fixes #50689
* obsoletes #50623

r? @nikomatsakis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants