Skip to content

Commit

Permalink
Deduce facet values for arguments to generic fns receiving a facet ty…
Browse files Browse the repository at this point in the history
…pe (carbon-language#4865)

The fn receiving a facet type needs to deduce a type from a
FacetAccessType, which is the SemIR type representing the parameter type
that is a generic parameter. For example:

```
  fn F[T: Interface](val: T);
```

Here T is a generic parameter that is a facet value, but the `val`
parameter's type is the facet value converted from a FacetType to a
TypeType, with `as type`. The result of that conversion is a
FacetAccessType. So the deduction code sees a FacetAccessType for the
type of `val`.

We make deduction undo the `as type` conversion to move back to the `T`
parameter declaration, which has type FacetType in order to deduce the
required facet value (which is itself a type constrained by the
FacetType). And when we have a facet value (of type FacetType) to be
deduced, we will also convert the argument if it is of an appropriate
type to a FacetValue that matches the FacetType using the changes from
PR carbon-language#4863.

Tests with `impl forall` can cause impl deduction to recurse forever and
crash, so those tests are omitted in this PR and they will come in
follow-up work that address the infinite recursion.

Rebased on top of PR carbon-language#4885
  • Loading branch information
danakj authored Feb 11, 2025
1 parent d0fec30 commit 316a6c5
Show file tree
Hide file tree
Showing 8 changed files with 2,347 additions and 154 deletions.
18 changes: 17 additions & 1 deletion toolchain/check/deduce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ auto DeductionContext::Deduce() -> bool {
DiagnosticAnnotationScope annotate_diagnostics(
&context().emitter(),
[&](auto& builder) { NoteInitializingParam(param_id, builder); });
// TODO: The call logic should reuse the conversion here (if any) instead
// of doing the same conversion again. At the moment we throw away the
// converted arg_id.
arg_id = ConvertToValueOfType(context(), loc_id_, arg_id, param_type_id);
if (arg_id == SemIR::ErrorInst::SingletonInstId) {
return false;
Expand Down Expand Up @@ -435,6 +438,20 @@ auto DeductionContext::Deduce() -> bool {
// TODO: Match field name order between param and arg.
break;

case SemIR::FacetAccessType::Kind:
// Given `fn F[G:! Interface](g: G)`, the type of `g` is `G as type`.
// `G` is a symbolic binding, whose type is a facet type, but `G as
// type` converts into a `FacetAccessType`.
//
// When we see a `FacetAccessType` parameter here, we want to deduce the
// facet type of `G`, not `G as type`, for the argument (so that the
// argument would be a facet value, whose type is the same facet type of
// `G`. So here we "undo" the `as type` operation that's built into the
// `g` parameter's type.
Add(param_inst.As<SemIR::FacetAccessType>().facet_value_inst_id, arg_id,
needs_substitution);
continue;

// TODO: Handle more cases.

default:
Expand Down Expand Up @@ -534,7 +551,6 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
auto param_type_const_id = SubstConstant(
context(), binding_type_id.AsConstantId(), substitutions_);
CARBON_CHECK(param_type_const_id.has_value());
CARBON_CHECK(!param_type_const_id.is_symbolic());
binding_type_id = context().GetTypeIdForTypeConstant(param_type_const_id);

// TODO: Suppress diagnostics here if `diagnose_` is false.
Expand Down
16 changes: 13 additions & 3 deletions toolchain/check/impl_lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,20 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
}
}

// TODO: Add a better impl lookup system. At the very least, we should only be
// considering impls that are for the same interface we're querying. We can
// also skip impls that mention any types that aren't part of our impl query.
for (const auto& impl : context.impls().array_ref()) {
// If impl.constraint_id is not symbolic, and doesn't match the query, then
// we don't need to proceed.
auto impl_interface_const_id =
context.constant_values().Get(impl.constraint_id);
if (!impl_interface_const_id.is_symbolic() &&
interface_const_id != impl_interface_const_id) {
continue;
}

// TODO: If the interface id of the `impl` and the query are not the same,
// then we can skip this `impl`. (The interface id is the root of the
// constraint, the unique `interface` declaration.)

auto specific_id = SemIR::SpecificId::None;
if (impl.generic_id.has_value()) {
specific_id = DeduceImplArguments(context, loc_id, impl, type_const_id,
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit 316a6c5

Please sign in to comment.