-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Make FacetAccessType evaluate to SymbolicBindingType for type-of a BindSymbolicName #6115
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
Conversation
Based on #6113, first commit is |
c629cab
to
d1f58ab
Compare
0d4947f
to
80f3f92
Compare
if (auto value = | ||
eval_context.GetCompileTimeBindValue(bind_name.bind_index()); | ||
value.has_value()) { | ||
auto value_inst_id = eval_context.constant_values().GetInstId(value); | ||
if (auto facet = | ||
eval_context.insts().TryGetAs<SemIR::FacetValue>(value_inst_id)) { | ||
// If the symbolic binding is replaced by a FacetValue (type + witness), | ||
// evaluate to its type component. | ||
return eval_context.constant_values().Get(facet->type_inst_id); | ||
} |
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.
May be worth at least noting in a comment that this is effectively forming and evaluating a FacetAccessType
? Given the small amount of duplicated code here I guess it's probably not worth attempting to share the implementation.
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.
Yeah that's a good point to add, done. I hadn't considered sharing code by constructing said FacetAccessType. But for now I have just made it clear it's doing that operation.
toolchain/check/testdata/facet/convert_facet_type_to_facet_value.carbon
Outdated
Show resolved
Hide resolved
impl Goat as Animal {} | ||
|
||
fn Feed[Food:! Edible, T:! Eats(Food)](e: T, food: Food) {} | ||
// CHECK:STDERR: convert_facet_value_value_to_generic_facet_value_value.carbon:[[@LINE+7]]:64: error: cannot convert type `T` that implements `Animal` into type implementing `Eats(Food)` [ConversionFailureFacetToFacet] |
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.
Is this diagnostic expected? (If so I guess we need to rename this to fail_todo_
)
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.
No, thanks for spotting it.
Interestingly, if I change Eats
parameter from Food:! type
to Food:! Edible
the error goes away. But I still don't think the error is correct. The impls constrain the parameter given to Eats
to be Edible
, but they also should work when Eats
only requires type
.
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.
Details here: https://discord.com/channels/655572317891461132/941071822756143115/1423429988748623934
The tl;dr is that in deduce we substitute deduced arguments from previous bindings into future bindings. If your future binding has something like U(T as type)
, so there's a SymbolicBindingType
in its specific, then we try to substitute the deduced argument for T
into it, which is a facet value. That can replace the BindSymbolicName
along with its EntityNameId
, which would then cause FacetAccessType
to be rebuilt too, but it doesn't do anything to the EntityNameId
in SymbolicBindingType
, and we don't have a substitution for the as type part of that. So we have to do something like what eval is doing to SymbolicBindingType
. But we can't modify a SymbolicBindingType
in the same way, so we have to make a new FacetAccessType
with the replacement that gets evaluated back into a SymbolicBindingType
.
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 to confirm, this was fixed, right?
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.
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.
uploading progress so far
if (auto value = | ||
eval_context.GetCompileTimeBindValue(bind_name.bind_index()); | ||
value.has_value()) { | ||
auto value_inst_id = eval_context.constant_values().GetInstId(value); | ||
if (auto facet = | ||
eval_context.insts().TryGetAs<SemIR::FacetValue>(value_inst_id)) { | ||
// If the symbolic binding is replaced by a FacetValue (type + witness), | ||
// evaluate to its type component. | ||
return eval_context.constant_values().Get(facet->type_inst_id); | ||
} |
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.
Yeah that's a good point to add, done. I hadn't considered sharing code by constructing said FacetAccessType. But for now I have just made it clear it's doing that operation.
toolchain/check/testdata/facet/convert_facet_type_to_facet_value.carbon
Outdated
Show resolved
Hide resolved
impl Goat as Animal {} | ||
|
||
fn Feed[Food:! Edible, T:! Eats(Food)](e: T, food: Food) {} | ||
// CHECK:STDERR: convert_facet_value_value_to_generic_facet_value_value.carbon:[[@LINE+7]]:64: error: cannot convert type `T` that implements `Animal` into type implementing `Eats(Food)` [ConversionFailureFacetToFacet] |
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.
No, thanks for spotting it.
Interestingly, if I change Eats
parameter from Food:! type
to Food:! Edible
the error goes away. But I still don't think the error is correct. The impls constrain the parameter given to Eats
to be Edible
, but they also should work when Eats
only requires type
.
(not ready for more review yet) |
Previously it performed two kinds of operations, with a boolean parameter to control whether it would unwrap FacetValue or not. This made the function hard to explain as "canonicalization". Now the contract of GetCanonicalFacetOrTypeValue is as follows: 1. For a facet value expression, it returns the canonical value of the facet value. 2. For a `<facet value> as type` it returns the canonical value of the `<facet value>`. 3. For other type expressions, it returns the canonical value of the type. 1 and 2 together collapse together two representations of a facet value (as a FacetType or as a TypeType) into a single canonical value, which is important for constant comparison of facet values where the `as type` is not meant to change the result. This is the case in impl lookups and `.Self` comparisons. The step of unwrapping `FacetValue` is only useful in the constant evaluation of `LookupImplWitness` and is used to collapse *symbolic* queries on `FacetValue(T)` and on `T` down to a single canonical value, since they produce the same result later when `T` is replaced with a facet value or type that can provide a concrete witness. This is now extensively documented in the constant evaluation of `LookupImplWitness`. This change came out of a request/discussion in #6115 (see comment #6115 (comment)).
a132167
to
2d59767
Compare
I addressed a bunch of the comments here in other PRs, so I have rebased this on top of them (most of which have already landed). The first commit is still |
I've marked this for review again by @zygoloid but @geoffromer would you mind to pick up the review from where he had left off? |
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.
First batch of comments; I'm still reviewing the testdata.
toolchain/check/import_ref.cpp
Outdated
// canonical EntityName (even if they are present there). | ||
// | ||
// No new work is generated by calling this function. | ||
static auto GetLocalEntityNameId(ImportContext& context, |
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.
It looks like this function is only used with the names of symbolic bindings, and the implementation seems to assume that as well (e.g. using AddSymbolicBindingName
, and omitting the NameScopeId
). Should the name and comment explicitly restrict it to those names?
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.
Yeah, most places don't import EntityNameId at all. We only import them for symbolic bindings. Putting something in the name here won't hurt.
Though note that the NameScopeId is not part of the canonical EntityNameId (see the comment on the function). So the other fields are not imported because we import canonical values.
impl Goat as Animal {} | ||
|
||
fn Feed[Food:! Edible, T:! Eats(Food)](e: T, food: Food) {} | ||
// CHECK:STDERR: convert_facet_value_value_to_generic_facet_value_value.carbon:[[@LINE+7]]:64: error: cannot convert type `T` that implements `Animal` into type implementing `Eats(Food)` [ConversionFailureFacetToFacet] |
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 to confirm, this was fixed, right?
toolchain/check/testdata/facet/convert_facet_value_value_to_generic_facet_value_value.carbon
Show resolved
Hide resolved
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.
PTAL
The SymbolicBindingType refers to the type value that will be substituted in for the BindSymbolicName, but holds onto the EntityNameId from the BindSymbolicName instead of (or in addition to, for now) the instruction.
The EntityNameId will be used to look in the ScopeStack to find the witnesses either from the BindSymbolicName instruction, or other instructions that specify
impls
constraints against the EntityName.This will allow us to have the
T
inI(T)
resolve to a.Self
reference in the type so that we get type equality with the binding's type:T:! I(.Self)
.