Use a "type structure" of each impl to choose the best match (#5124)

The type structure is built for the impl lookup query for the
combination of the self type and the interface being queried. Then it is
built for each `impl` definition that is a potential candidate.

The type structures are compared to ensure they have a compatible
structure, and the `impl` declaration is not considered if they do not.

Finally, the type structures are used as a sorting key for the candidate
`impl` declarations, with the most-specified type structures (the ones
with the furthest distance to the first symbolic value) coming first in
the ordering.

See
https://docs.carbon-lang.dev/docs/design/generics/overview.html#parameterized-impl-declarations
for the design of the type structure and related ordering.

Most of the commits in this PR landed in #5158 (11ae0e27ab) by
mistake, but this includes the final changes since that PR was written.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Dana Jansens
2025-03-21 15:27:45 +00:00
committed by GitHub
co-authored by Richard Smith
parent 9131b3a0de
commit 6041e9aa9d
2 changed files with 17 additions and 46 deletions
@@ -15,10 +15,6 @@ library "[[@TEST_NAME]]";
// Check that we get a reasonable diagnostic for an unimplemented operation on a
// template dependent expression.
fn F[template T:! type](x: T) -> i32 {
// CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
// CHECK:STDERR: return x.n * 3;
// CHECK:STDERR: ^~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
// CHECK:STDERR: return x.n * 3;
// CHECK:STDERR: ^~~~~~~
@@ -38,10 +34,6 @@ class C {
// template dependent value where the type is concrete but determined through
// the template dependent value.
fn F(template c:! C) -> i32 {
// CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
// CHECK:STDERR: return c.n * 3;
// CHECK:STDERR: ^~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
// CHECK:STDERR: return c.n * 3;
// CHECK:STDERR: ^~~~~~~
@@ -126,15 +118,15 @@ fn F[template T:! type](x: T) {
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type @F.%T.loc6_15.2 (%T) [template = %require_complete (constants.%require_complete.4ae)]
// CHECK:STDOUT: %.loc15_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc6_15.2 (%T) [template]
// CHECK:STDOUT: %.loc15_11.4: <instruction> = access_member_action %.loc15_11.1, n [template]
// CHECK:STDOUT: %.loc15_11.5: type = type_of_inst %.loc15_11.4 [template]
// CHECK:STDOUT: %.loc11_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc6_15.2 (%T) [template]
// CHECK:STDOUT: %.loc11_11.4: <instruction> = access_member_action %.loc11_11.1, n [template]
// CHECK:STDOUT: %.loc11_11.5: type = type_of_inst %.loc11_11.4 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: fn[%T.patt.loc6_15.1: type](%x.param_patt: @F.%T.loc6_15.2 (%T)) -> %i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %x.ref: @F.%T.loc6_15.2 (%T) = name_ref x, %x
// CHECK:STDOUT: %.loc15_11.1: @F.%T.loc6_15.2 (%T) = splice_inst %.loc15_11.3
// CHECK:STDOUT: %.loc15_11.2: @F.%.loc15_11.5 (@F.%.loc15_11.5) = splice_inst %.loc15_11.4
// CHECK:STDOUT: %.loc11_11.1: @F.%T.loc6_15.2 (%T) = splice_inst %.loc11_11.3
// CHECK:STDOUT: %.loc11_11.2: @F.%.loc11_11.5 (@F.%.loc11_11.5) = splice_inst %.loc11_11.4
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
// CHECK:STDOUT: return <error>
// CHECK:STDOUT: }
@@ -211,13 +203,13 @@ fn F[template T:! type](x: T) {
// CHECK:STDOUT: %c.patt.loc11_15.2: %C = symbolic_binding_pattern c, 0, template [template = %c.patt.loc11_15.2 (constants.%c.patt)]
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %.loc20_11.2: <instruction> = access_member_action %c.ref, n [template]
// CHECK:STDOUT: %.loc20_11.3: type = type_of_inst %.loc20_11.2 [template]
// CHECK:STDOUT: %.loc16_11.2: <instruction> = access_member_action %c.ref, n [template]
// CHECK:STDOUT: %.loc16_11.3: type = type_of_inst %.loc16_11.2 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: fn(%c.patt.loc11_15.1: %C) -> %i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %c.ref: %C = name_ref c, %c.loc11_15.1 [template = %c.loc11_15.2 (constants.%c)]
// CHECK:STDOUT: %.loc20_11.1: @F.%.loc20_11.3 (@F.%.loc20_11.3) = splice_inst %.loc20_11.2
// CHECK:STDOUT: %.loc16_11.1: @F.%.loc16_11.3 (@F.%.loc16_11.3) = splice_inst %.loc16_11.2
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
// CHECK:STDOUT: return <error>
// CHECK:STDOUT: }
+9 -30
View File
@@ -185,6 +185,15 @@ class TypeStructureBuilder {
Push(SymbolicType());
break;
}
case SemIR::TypeOfInst::Kind: {
// TODO: For a template value with a fixed type, such as `template n:!
// i32`, we could look at the type of the value to see if it's
// template-dependent (which it's not here) and add that type to the
// type structure?
// https://github.com/carbon-language/carbon-lang/pull/5124#discussion_r2006617038
Push(SymbolicType());
break;
}
// ==== Concrete types ====
@@ -287,36 +296,6 @@ class TypeStructureBuilder {
}
break;
}
case CARBON_KIND(SemIR::TypeOfInst type_of): {
(void)type_of;
auto const_id = context_.constant_values().Get(inst_id);
// TODO: TypeOfInst should not be encountered in impl lookup with a
// template-dependent value, since impl lookup on such values should
// be deferred to type-checking the specific. So this should become a
// CARBON_FATAL code path. For now it is possible, though, to reach
// here and it results in a diagnostic.
//
// However, TypeOfInst can be determined to be a concrete value for a
// template value with a non-template dependent type. This currently
// does not happen though, the TypeOfInst is always template
// dependent. If it does in the future then we will want to use the
// concrete constant value instruction of `inst_id` in the type
// structure:
// if (const_id.is_concrete()) {
// PushInstId(context_.constant_values().GetInstId(const_id));
// }
//
// If TypeOfInst was used for more general metaprogramming then we may
// need to handle both concrete and perhaps symbolic (non-template)
// values of TypeOfInst.
CARBON_CHECK(const_id.is_symbolic());
auto sym = context_.constant_values().GetSymbolicConstant(const_id);
CARBON_CHECK(sym.dependence == SemIR::ConstantDependence::Template,
"TypeOfInst with non-template symbolic value?");
context_.TODO(context_.insts().GetLocId(inst_id),
"Impl lookup on template-dependent type value");
break;
}
default:
CARBON_FATAL("Unhandled type instruction {0}", inst_id);
}