-
Notifications
You must be signed in to change notification settings - Fork 181
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
Support const evaulation #596
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a quick first pass.
chalk-engine/src/slg/aggregate.rs
Outdated
@@ -483,6 +483,44 @@ impl<I: Interner> AntiUnifier<'_, '_, I> { | |||
} | |||
} | |||
|
|||
(ConstValue::Concrete(e), ConstValue::Unevaluated(u)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial thought: Will we ever actually give an answer with an Unevaluated
const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why not. Chalk can return associated type projections in answers, and an unevalutated const is basically a projection.
chalk-engine/src/slg/resolvent.rs
Outdated
match u.try_eval(answer_ty, interner) { | ||
Ok(ev) => assert!(c.const_eq(answer_ty, &ev, interner)), | ||
|
||
Err(ConstEvalError::TooGeneric) => panic!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here...feels like we should have already have caught this before and the answer itself (or the goal) shouldn't have unevaluated consts. (Well...maybe the goal would?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out that unevaluated const expressions can appear in the answer and the goal in the resolvent if we so much as write impl Foo<3?> for Baz {}
.
tests/test/constants.rs
Outdated
S<N>: Trait | ||
} | ||
} yields { | ||
"Unique; substitution [?0 := 3?], lifetime constraints []" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, interesting. I have to think if this is something we want to be able to support. (Giving solutions with unevaluated consts). It's pretty clear that these would have to be evaluable, since they would have to pass WF/coherence checks.
tests/test/constants.rs
Outdated
goal { | ||
S<?>: Trait | ||
} yields { | ||
"Unique" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See, this should be Ambiguous
, for a similar reason that two InferenceVar
s aren't the same: we don't actually know this would evaluate to the same const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been assuming that for chalk testing purposes that ?
evaluates to a specific but unknown constant. I don't feel like breaking reflexivity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that's not true. The point here is that ?
represents a constant that can't be evaluated yet. Importantly, if you have two constants that can't be evaluated (e.g. {N+1}
and {X +1}
), then we can't say that if there is a solution that covers one case, there is a solution that covers all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it turns out, removing const_eq
for unevaluated consts and assuming that two ?
s don't necessarily evaluate to the same value breaks LocalImplAllowed(Baz: Foo<?>)
in the unevaluated_const_unknown
coherence test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably okay and what we expect :) That can be a lowering_error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be weird for it to fail because it violated the orphan rules, though? impl Foo<?> for Baz
isn't violating the orphan rules because Foo
is local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I never responded to this. Sorry about that.
So, I think theoretically we could always say that impl Foo<?0> for Baz {}
never violates orphan rules, since both Foo
and Baz
are local. However, I think for unevaluated consts, it's okay to say it's Ambiguous
if we get TooGeneric
. I would have to look more into the orphan rules code to figure out how to best handle this. For this PR, we can mark a comment or FIXME
and file a followup issue for more discussion.
tests/test/coherence.rs
Outdated
// FIXME: The following test fails by violation of the orphan rules. | ||
// This is because right now, unevaluated const expressions have a | ||
// temporary reflexivity hole, where ? != ?, | ||
// which messes with the SLG resolvent. | ||
// The test should be uncommented when the reflexivity hole is fixed. | ||
// It is supposed to fail because of overlapping impls. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above. What error do we get here if we leave these uncommented? With these, we actually don't know that these are overlapping impls (could impl Foo<3>
and impl Foo<4>
). But this still shouldn't lower successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now it's structural mismatch between answer '?' and pending goal '?'
. Is the resolvent allowed to create a CannotProve
goal? Also, in the case where chalk can't prove that impls don't overlap, it assumes that they do.
☔ The latest upstream changes (presumably #606) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
/// `InternedUnevaluatedConst` instances are not created by chalk, | ||
/// it can only evaluate them with the [`try_eval_const`] method | ||
/// and check for (syntactic for now) equality between them. | ||
type InternedUnevaluatedConst: Debug + Clone + Eq + Hash; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we anticipate that this will carry a "substitutions"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect not, right?
&self, | ||
ty: &Self::InternedType, | ||
constant: &Self::InternedUnevaluatedConst, | ||
) -> Result<Self::InternedConcreteConst, ConstEvalError>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kind of expect this to take a set of substitutions. Consider how the rustc variant looks.
// Note that we assume that the unevaluated const doesn't contain inference variables. | ||
// Also, for now, the unevalutable unevaluated const is too generic. | ||
(&ConstValue::InferenceVar(_), &ConstValue::Unevaluated(_)) | ||
| (&ConstValue::Unevaluated(_), &ConstValue::InferenceVar(_)) => Ok(self.goals.push( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think I expected us to unify an inference variable here...
@@ -440,9 +452,23 @@ impl<'t, I: Interner> Unifier<'t, I> { | |||
} | |||
} | |||
|
|||
// For now, the unevalutable unevaluated const is too generic. | |||
(&ConstValue::Concrete(_), &ConstValue::Unevaluated(_)) | |||
| (&ConstValue::Unevaluated(_), &ConstValue::Concrete(_)) => Ok(self.goals.push( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...and then to generate a subobligation here, kind of like associated type normalization. Is this planned for "future PRs" or is my idea of how to approach this outdated :)
} | ||
} | ||
|
||
// FIXME: The following test fails by structural mismatch of ? and ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this FIXME mean exactly?
We discussed this in our meeting Tuesday. The conclusion was that the PR as-is isn't correct (see @nikomatsakis's review). Part of this is we need better direction for how consts will be handled. I'm going to go ahead and close this for now. @josh65536 if you still want to work on this, feel free to reopen. Either Niko or I can answer questions on here or, preferably, Zulip. We really would like to get this over the finish line :) |
Fixes #548
For Chalk's tests, a special-formatted
Option<u32>
is used, whereSome(n)
evaluates ton
andNone
evaluates to a specific but unknown constant.Some(n)
is formatted asn?
andNone
is formatted as?
.