Support unqualified lookup into extended interfaces from classes and impls (#7217)

When a class extends an interface, referring to a member name of the
interface as an unqualified name should refer to the class's
corresponding associated entity value, not to the associated entity
itself. Similarly, in an `impl`, unqualified names of associated
entities should refer to the `impl`'s corresponding value for that
entity.

To support this, we treat `impl`s as `extend`ing their implemented facet
type, and we make lookups into an extended facet type use the `Self`
type of the extending `impl` or `class` if lookup finds an associated
entity. We already did the latter if the extending entity was an
interface; this extends the existing support for these other cases.
This commit is contained in:
Richard Smith
2026-05-19 18:01:54 +00:00
committed by GitHub
parent 0d7c4a99eb
commit ce080b3549
112 changed files with 962 additions and 4125 deletions
@@ -389,6 +389,7 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
// CHECK:STDOUT: .T = <poisoned>
// CHECK:STDOUT: .T = <poisoned>
// CHECK:STDOUT: .Convert = @ImplicitAs.WithSelf.%assoc0.loc16_32.1
// CHECK:STDOUT: .IntLiteral = <poisoned>
// CHECK:STDOUT: witness = (@ImplicitAs.WithSelf.%ImplicitAs.WithSelf.Convert.decl)
// CHECK:STDOUT:
// CHECK:STDOUT: !requires:
@@ -420,6 +421,7 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %i32.builtin.as.AddWith.impl.Op.decl
// CHECK:STDOUT: extend %AddWith.type
// CHECK:STDOUT: witness = %AddWith.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -443,6 +445,7 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Convert = %Core.IntLiteral.as.As.impl.Convert.decl
// CHECK:STDOUT: extend %As.type
// CHECK:STDOUT: witness = %As.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -466,6 +469,7 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Convert = %Core.IntLiteral.as.ImplicitAs.impl.Convert.decl
// CHECK:STDOUT: extend %ImplicitAs.type
// CHECK:STDOUT: witness = %ImplicitAs.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -493,6 +497,7 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .IntLiteral = <poisoned>
// CHECK:STDOUT: .Convert = %i32.builtin.as.ImplicitAs.impl.Convert.decl
// CHECK:STDOUT: extend %ImplicitAs.type
// CHECK:STDOUT: witness = %ImplicitAs.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -256,6 +256,7 @@ var arr: array(i32, (1 as i32).(I.F)(2));
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %i32.as.I.impl.F.decl
// CHECK:STDOUT: extend %I.ref
// CHECK:STDOUT: witness = %I.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -95,12 +95,7 @@ alias N.F2 = F1;
// CHECK:STDERR:
fn N.F1();
// Failure: `N.F1` used after declaration failed.
// TODO: #4622 - Allow defining a poisoned name so it would be found if used after it's declared.
// CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:14: error: member name `F1` not found in `N` [MemberNameNotFoundInInstScope]
// CHECK:STDERR: alias N.F3 = N.F1;
// CHECK:STDERR: ^~~~
// CHECK:STDERR:
// Don't produce an additional diagnostic here.
alias N.F3 = N.F1;
// --- fail_poison_multiple_scopes.carbon
@@ -216,6 +216,7 @@ fn G() {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %ImplsGeneric.as.Generic.impl.F.decl
// CHECK:STDOUT: extend %Generic.type
// CHECK:STDOUT: witness = %Generic.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -226,6 +227,7 @@ fn G() {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .G = %ImplsGeneric.as.Other.impl.G.decl
// CHECK:STDOUT: extend %Other.ref
// CHECK:STDOUT: witness = %Other.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -1785,6 +1785,7 @@ fn F() {
// CHECK:STDOUT: %Z.impl_witness: <witness> = impl_witness %Z.impl_witness_table [concrete = constants.%Z.impl_witness.c05]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %Z.ref
// CHECK:STDOUT: witness = %Z.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -1800,6 +1801,7 @@ fn F() {
// CHECK:STDOUT: %Z.impl_witness.loc9_35.1: <witness> = impl_witness %Z.impl_witness_table, @DD.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc9_35.2 (constants.%Z.impl_witness.437)]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %Z.ref
// CHECK:STDOUT: witness = %Z.impl_witness.loc9_35.1
// CHECK:STDOUT: }
// CHECK:STDOUT: }
@@ -1820,6 +1822,7 @@ fn F() {
// CHECK:STDOUT: %Z.impl_witness.loc12_39.1: <witness> = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc12_39.2 (constants.%Z.impl_witness.40e)]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %Z.ref
// CHECK:STDOUT: witness = %Z.impl_witness.loc12_39.1
// CHECK:STDOUT: }
// CHECK:STDOUT: }
@@ -193,6 +193,7 @@ fn F() {
// CHECK:STDOUT: %Y.impl_witness: <witness> = impl_witness %Y.impl_witness_table [concrete = constants.%Y.impl_witness]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %Y.ref
// CHECK:STDOUT: witness = %Y.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -201,6 +202,7 @@ fn F() {
// CHECK:STDOUT: %W.impl_witness: <witness> = impl_witness %W.impl_witness_table [concrete = constants.%W.impl_witness]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %W.ref
// CHECK:STDOUT: witness = %W.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -219,6 +221,7 @@ fn F() {
// CHECK:STDOUT: %Z.impl_witness.loc19_36.1: <witness> = impl_witness %Z.impl_witness_table, @CC.as.Z.impl(constants.%E) [symbolic = %Z.impl_witness.loc19_36.2 (constants.%Z.impl_witness.228)]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: extend %Z.ref
// CHECK:STDOUT: witness = %Z.impl_witness.loc19_36.1
// CHECK:STDOUT: }
// CHECK:STDOUT: }