Skip to content

Conversation

danakj
Copy link
Contributor

@danakj danakj commented Sep 22, 2025

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 in I(T) resolve to a .Self reference in the type so that we get type equality with the binding's type: T:! I(.Self).

@danakj
Copy link
Contributor Author

danakj commented Sep 22, 2025

Based on #6113, first commit is symbolic-binding-type

@github-actions github-actions bot requested a review from dwblaikie September 22, 2025 22:20
@danakj danakj requested review from zygoloid and removed request for dwblaikie September 22, 2025 22:20
@danakj danakj force-pushed the symbolic-binding-type branch from c629cab to d1f58ab Compare September 22, 2025 22:25
@danakj danakj force-pushed the symbolic-binding-type branch from d1f58ab to 0d4947f Compare September 23, 2025 15:14
@danakj danakj force-pushed the symbolic-binding-type branch from 0d4947f to 80f3f92 Compare September 23, 2025 15:16
Comment on lines 293 to 322
auto GetCanonicalizedFacetTypeOrType(Context& context, SemIR::InstId inst_id)
-> SemIR::TypeInstId {
inst_id = GetCanonicalizedFacetOrTypeValue(context, inst_id);
auto type_id = context.insts().Get(inst_id).type_id();
if (context.types().Is<SemIR::TypeType>(type_id)) {
return context.types().GetAsTypeInstId(inst_id);
} else {
CARBON_CHECK(context.types().Is<SemIR::FacetType>(type_id));
return context.types().GetInstId(type_id);
}
}

auto MoveToFacetValue(Context& context, SemIR::InstId inst_id)
-> SemIR::InstId {
auto type_id = context.insts().Get(inst_id).type_id();
if (context.types().Is<SemIR::SymbolicBindingType>(type_id)) {
return GetCanonicalizedFacetOrTypeValue(context,
context.types().GetInstId(type_id));
}
return inst_id;
}

auto MoveToFacetType(Context& context, SemIR::InstId inst_id) -> SemIR::InstId {
auto facet_value_id = GetCanonicalizedFacetOrTypeValue(context, inst_id);
auto type_id = context.insts().Get(facet_value_id).type_id();
if (context.types().Is<SemIR::FacetType>(type_id)) {
return context.types().GetInstId(type_id);
}
return facet_value_id;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are these three operations which all want to move to the type of a facet value but in slightly different ways or with slightly different inputs. I want to figure out a way to consolidate them though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have merged two of them, need to think more about how to shape things with the third still, but it's not fundamental to the PR.

Comment on lines +2191 to +2200
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);
}
Copy link
Contributor

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.

Comment on lines +217 to +218
// TODO: This is to be removed, at which point replace NewSamePhase with
// NewAnyPhase.
Copy link
Contributor

Choose a reason for hiding this comment

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

NewAnyPhase would deduce the phase from the operands, which would give Concrete, whereas we want this to always be symbolic. I think we'd need a little more machinery here, or maybe we can just keep using NewSamePhase?

}

CARBON_CHECK(!context.insts().Is<SemIR::FacetAccessType>(inst_id));
CARBON_CHECK(!context.insts().Is<SemIR::FacetValue>(inst_id));
CARBON_CHECK(!context.insts().Is<SemIR::SymbolicBindingType>(inst_id));

return context.constant_values().GetConstantInstId(inst_id);
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder: can this function be rephrased as first mapping to the constant inst id, and then doing some postprocessing steps if needed? (Eg, mapping from a facet value -> its type component, or mapping from a symbolic binding type -> its facet value, depending on whether we want a facet-flavored result or a type-flavored result.) I guess it's not entirely clear to me what the contract is here, but I'd have expected that two instructions with the same constant value ought to produce the same canonicalized facet or type value, and going to the constant value first would guarantee that is the case.

Comment on lines +301 to +315
auto GetTypeForFacetOrTypeValue(Context& context, SemIR::InstId inst_id)
-> SemIR::TypeInstId {
if (inst_id == SemIR::ErrorInst::InstId) {
return SemIR::ErrorInst::TypeInstId;
}

inst_id = GetCanonicalizedFacetOrTypeValue(context, inst_id);
auto type_id = context.insts().Get(inst_id).type_id();
if (context.types().Is<SemIR::TypeType>(type_id)) {
return context.types().GetAsTypeInstId(inst_id);
} else {
CARBON_CHECK(context.types().Is<SemIR::FacetType>(type_id));
return context.types().GetInstId(type_id);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm having trouble understanding what this function is doing. It looks like:

Given inst_id which is a facet value, perform some canonicalization step. Then:
If the type of inst_id is the type type, then return inst_id.
If the type of inst_id is a facet type other than type, then return the type of inst_id.

I find that behavior surprising -- that it sometimes returns the value of inst_id, and sometimes returns the type of inst_id. Is that the intent here?

Comment on lines +117 to +119
// If `preserve_facet_value` is false, the `FacetValue` instruction is unwrapped
// to get to an underlying canonical type instruction. Otherwise, the output is
// either the original instruction or a facet value instruction.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I'm misunderstanding something, but it looks to me like this comment doesn't match the implementation. Specifically, I think if we're given a SymbolicBindingType, we'll always produce a facet value regardless of preserve_facet_value, and won't produce a canoncial type instruction (which would be the SymbolicBindingType).

Feed(Goat as (Animal & Climbs));

// CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE+7]]:3: error: cannot convert type `Animal & Climbs` into type implementing `Eats` [ConversionFailureTypeToFacet]
// CHECK:STDERR: fail_convert_multi_interface_facet_value_to_facet_value.carbon:[[@LINE+7]]:3: error: cannot convert type `Animal & Climbs` that implements `Animal & Climbs` into type implementing `Eats` [ConversionFailureFacetToFacet]
Copy link
Contributor

Choose a reason for hiding this comment

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

This diagnostic doesn't look right to me.

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]
Copy link
Contributor

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_)

danakj and others added 2 commits September 29, 2025 16:51
Co-authored-by: Richard Smith <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants