Type completeness in extend (#6395)

Define rules for `extend` declarations (`extend require`, `extend impl
as`, `extend base`, `extend adapt`) that say the target scope they name
must be complete at the point of the declaration. Define completeness
for a facet type to include all interfaces and named constraints that
provide unqualified name lookup through the facet type.

---------

Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Dana Jansens
2025-11-25 22:45:34 +00:00
committed by GitHub
co-authored by Carbon Infra Bot Richard Smith
parent ec8c999bb1
commit 0baa74d96f
2 changed files with 630 additions and 13 deletions
+190
View File
@@ -0,0 +1,190 @@
# Type completeness in extend
<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->
[Pull request](https://github.com/carbon-language/carbon-lang/pull/6395)
<!-- toc -->
## Table of contents
- [Abstract](#abstract)
- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Details](#details)
- [Rationale](#rationale)
- [Alternatives considered](#alternatives-considered)
<!-- tocstop -->
## Abstract
Require any target scopes in an `extend` declaration to be complete, since
`extend` changes the scopes which are looked in for name lookup to include those
named in the declaration.
## Problem
Proposal
[#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md)
laid out rules for when a facet type needs to be identified or complete. When
`require impls X` is written, then `X` must be identified. However it does not
specify any rule for `extend require impls X`. And we lack completeness rules
for other `extend` declarations, including `extend impl as X`, `extend base: X`,
and `extend adapt X`.
An `extend` declaration always names one or more target entities which will be
included in the search for names when looking in the enclosing scope of the
`extend` declaration. In order to do name lookup into an entity, it must be
complete to avoid poisoning names in the entity.
## Background
- Proposal
[#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md):
Forward `impl` declaration of an incomplete interface
- Proposal
[#2760](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p2760.md):
Consistent `class` and `interface` syntax
- Proposal
[#0777](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0777.md):
Inheritance
## Proposal
An `extend` declaration declares that the enclosing scope extends the scope
associated with any target entity named in the declaration. That is to say name
lookups into the enclosing scope will also look into the scopes which are
nominated by the `extend` declaration. The `extend` declaration requires that
the target scopes named in an `extend` declaration are complete, or if the
target is a generic parameter, requires the type of the parameter to be
complete.
The scope of a facet type formed by a `where` declaration extends the scope of
its first operand. And the scope of a facet type formed by a `&` operator
extends the scopes of both of its operands. In either case, the scope of the
facet type is complete if every scope it extends is complete. The facet type
`type` is associated with an empty scope and is complete.
| Facet type | Requirement |
| ---------------------------- | ------------------------------------- |
| `I` | Requires `I` is complete. |
| `I & J` | Requires `I` and `J` are complete. |
| `type where U impls J` | Requires `type` is complete. |
| `I & (type where U impls J)` | Requires `I` and `type` are complete. |
## Details
To `extend` an entity `Y` with another `Z` means that name lookups into `Y` will
also look into `Z`. Immediately after the `extend` operation, members of `Z`
should also be found when doing name lookup into `Y`, both from outside and from
inside the definition of `Y`. In order to be able to perform lookups into `Z`,
we require that `extend` operations only target scopes that are complete.
This requirement functions recursively. Given an interface `B` that extends
another interface `A`: By naming `A` in an extend declaration, we require `A` is
complete. This provides that its entire definition is known, and thus its
`extend` relationship to `B`. The `extend` relationship there also provides that
`B` is complete.
If the target scope of an `extend` declaration is a generic parameter, its type
must be complete where the `extend` declaration is written, as name lookups into
the extended scope will look into the type of the generic parameter.
```carbon
interface I {
fn F();
}
class C(T:! I) {
extend base: T;
// `F` names `T.F` here, found in `I`.
fn G() { F(); }
}
```
As any generic parameter in the enclosing scope is replaced by a more specific
value, extended scopes that depend on a generic parameter must remain complete.
This includes forming a specific for the extended scope involving the parameter
in order to surface any monomorphization errors in the resulting specific.
In the next example, the `extend` declaration in interface `A(N)` names a
symbolic facet type which can produce monomorphization errors when a negative
value is provided for `N`. When a more specific value for the target `B(N)` is
provided, we require the specific value to be complete as well by forming the
specific. A diagnostic error would be produced while checking `C(-1)` for
completeness, as it requires `A(-1)` to be complete, which requires
`B(array(i32, -1))` to be complete, and that contains an invalid type.
```carbon
interface B(T:! type) {}
interface A(N:! i32) {
// Requires `B(N)` to be complete.
extend require impls B(array(i32, N)) {}
}
class C(N! i32) {
// Requires `A(N)` to be complete, which requires `B(N)` to be complete.
extend impl as A(N);
}
fn F() {
// Requires `C(-1)` to be complete, which requires `A(-1)` to be complete, which requires `B(array(i32, -1))` to be complete.
var c: C(-1);
}
```
These rules prohibit an `extend` declaration from naming its enclosing scope,
since by being part of the definition of that scope, it is implied that the
enclosing scope is not complete. This seems reasonable as all names available
inside the enclosing interface or named constraint are already available or
would conflict with the ones that are.
## Rationale
This is based on the principle of
[Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
For code to be easy to write, the rules need to be consistent. Once an `extend`
declaration has been written, the names inside should become available through
the enclosing scope immediately. If we allow an interface named in an `extend`
declaration to be incomplete, then name lookup will fail ambiguously. Those same
names may become valid later once the interface is defined.
## Alternatives considered
We considered not requiring that the scope named in an `extend` declaration be
complete where it is written, but only when the enclosing scope is required to
be complete. This is more flexible, allowing entities to be defined later in the
program. However this does not allow the use of names from the target scope in
the `extend` to be used from within the enclosing definition scope. They would
not become available until the enclosing definition scope was closed and
complete.
In particular, we want this to work:
```carbon
interface Z {
let X:! type;
fn F() -> X;
}
class C {
extend impl as Z where .X = () {
// Names `X` directly.
fn F() -> X;
}
// Names `X` and `F` directly.
fn G() -> X { return F(); }
}
// Now `C` is complete, `C.X` and `C.F` are also available.
fn H() -> C.X {
return C.F();
}
```
+440 -13
View File
@@ -141,6 +141,9 @@ interface J {
class E {
extend impl as J where .U = i32 {
// TODO: U should be in scope and usable as soon as we finish the
// declaration / enter the definition.
//
// CHECK:STDERR: fail_todo_use_associated_constant_in_impl.carbon:[[@LINE+14]]:13: error: cannot implicitly convert non-type value of type `<associated entity in J>` to `type` [ConversionFailureNonTypeToFacet]
// CHECK:STDERR: fn F(u: U) -> U {
// CHECK:STDERR: ^
@@ -161,6 +164,61 @@ class E {
}
}
// --- fail_todo_call_extend_fn.carbon
library "[[@TEST_NAME]]";
interface J {
fn F() {}
}
class E {
extend impl as J {
fn F() {}
}
// CHECK:STDERR: fail_todo_call_extend_fn.carbon:[[@LINE+4]]:12: error: value of type `<associated entity in J>` is not callable [CallToNonCallable]
// CHECK:STDERR: fn G() { F(); }
// CHECK:STDERR: ^~~
// CHECK:STDERR:
fn G() { F(); }
}
// --- call_extend_method.carbon
library "[[@TEST_NAME]]";
interface J {
fn F[self: Self]() {}
}
class E {
extend impl as J {
fn F[self: Self]() {}
}
fn G[self: Self]() { self.F(); }
}
// --- fail_todo_use_extend_constant.carbon
library "[[@TEST_NAME]]";
interface J {
let X:! type;
}
class E {
extend impl as J where .X = () {}
// TODO: `X` should be in scope and directly usable.
// CHECK:STDERR: fail_todo_use_extend_constant.carbon:[[@LINE+7]]:13: error: cannot implicitly convert non-type value of type `<associated entity in J>` to `type` [ConversionFailureNonTypeToFacet]
// CHECK:STDERR: fn G() -> X { return (); }
// CHECK:STDERR: ^
// CHECK:STDERR: fail_todo_use_extend_constant.carbon:[[@LINE+4]]:13: note: type `<associated entity in J>` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessNote]
// CHECK:STDERR: fn G() -> X { return (); }
// CHECK:STDERR: ^
// CHECK:STDERR:
fn G() -> X { return (); }
}
// --- fail_todo_self_period_associated_type.carbon
library "[[@TEST_NAME]]";
@@ -2309,11 +2367,11 @@ fn F() {
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness @E.%J.impl_witness_table [concrete]
// CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete]
// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type.3487c0.1: type = fn_type @E.as.J.impl.F.loc24_21.1 [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type.3487c0.1: type = fn_type @E.as.J.impl.F.loc27_21.1 [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.1487eb.1: %E.as.J.impl.F.type.3487c0.1 = struct_value () [concrete]
// CHECK:STDOUT: %J.facet: %J.type = facet_value %E, (%J.impl_witness) [concrete]
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type.3487c0.2: type = fn_type @E.as.J.impl.F.loc24_21.2 [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type.3487c0.2: type = fn_type @E.as.J.impl.F.loc27_21.2 [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.1487eb.2: %E.as.J.impl.F.type.3487c0.2 = struct_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -2379,24 +2437,24 @@ fn F() {
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @E.as.J.impl: %Self.ref as %.loc9_20 {
// CHECK:STDOUT: %E.as.J.impl.F.decl.loc24_21.1: %E.as.J.impl.F.type.3487c0.1 = fn_decl @E.as.J.impl.F.loc24_21.1 [concrete = constants.%E.as.J.impl.F.1487eb.1] {
// CHECK:STDOUT: %E.as.J.impl.F.decl.loc27_21.1: %E.as.J.impl.F.type.3487c0.1 = fn_decl @E.as.J.impl.F.loc27_21.1 [concrete = constants.%E.as.J.impl.F.1487eb.1] {
// CHECK:STDOUT: %u.patt: <error> = value_binding_pattern u [concrete]
// CHECK:STDOUT: %u.param_patt: <error> = value_param_pattern %u.patt, call_param0 [concrete]
// CHECK:STDOUT: %return.patt: <error> = return_slot_pattern [concrete]
// CHECK:STDOUT: %return.param_patt: <error> = out_param_pattern %return.patt, call_param1 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %U.ref.loc24_19: %J.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0.411]
// CHECK:STDOUT: %.loc24_19: type = converted %U.ref.loc24_19, <error> [concrete = <error>]
// CHECK:STDOUT: %U.ref.loc27_19: %J.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0.411]
// CHECK:STDOUT: %.loc27_19: type = converted %U.ref.loc27_19, <error> [concrete = <error>]
// CHECK:STDOUT: %u.param: <error> = value_param call_param0
// CHECK:STDOUT: %.1: <error> = splice_block <error> [concrete = <error>] {
// CHECK:STDOUT: %U.ref.loc24_13: %J.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0.411]
// CHECK:STDOUT: %.loc24_13: type = converted %U.ref.loc24_13, <error> [concrete = <error>]
// CHECK:STDOUT: %U.ref.loc27_13: %J.assoc_type = name_ref U, @U.%assoc0 [concrete = constants.%assoc0.411]
// CHECK:STDOUT: %.loc27_13: type = converted %U.ref.loc27_13, <error> [concrete = <error>]
// CHECK:STDOUT: }
// CHECK:STDOUT: %u: <error> = value_binding u, %u.param
// CHECK:STDOUT: %return.param: ref <error> = out_param call_param1
// CHECK:STDOUT: %return: ref <error> = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %E.as.J.impl.F.decl.loc24_21.2: %E.as.J.impl.F.type.3487c0.2 = fn_decl @E.as.J.impl.F.loc24_21.2 [concrete = constants.%E.as.J.impl.F.1487eb.2] {
// CHECK:STDOUT: %E.as.J.impl.F.decl.loc27_21.2: %E.as.J.impl.F.type.3487c0.2 = fn_decl @E.as.J.impl.F.loc27_21.2 [concrete = constants.%E.as.J.impl.F.1487eb.2] {
// CHECK:STDOUT: %u.patt: %pattern_type.7ce = value_binding_pattern u [concrete]
// CHECK:STDOUT: %u.param_patt: %pattern_type.7ce = value_param_pattern %u.patt, call_param0 [concrete]
// CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete]
@@ -2410,7 +2468,7 @@ fn F() {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .U = <poisoned>
// CHECK:STDOUT: .F = %E.as.J.impl.F.decl.loc24_21.1
// CHECK:STDOUT: .F = %E.as.J.impl.F.decl.loc27_21.1
// CHECK:STDOUT: witness = @E.%J.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -2431,7 +2489,7 @@ fn F() {
// CHECK:STDOUT: requirement_rewrite %impl.elem0, %i32
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant, @E.as.J.impl.%E.as.J.impl.F.decl.loc24_21.2), @E.as.J.impl [concrete]
// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant, @E.as.J.impl.%E.as.J.impl.F.decl.loc27_21.2), @E.as.J.impl [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness]
// CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357]
@@ -2453,15 +2511,15 @@ fn F() {
// CHECK:STDOUT: fn(%u.param: @J.F.%impl.elem0.loc5_11.1 (%impl.elem0.fa9)) -> @J.F.%impl.elem0.loc5_11.1 (%impl.elem0.fa9);
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.as.J.impl.F.loc24_21.1(%u.param: <error>) -> <error> {
// CHECK:STDOUT: fn @E.as.J.impl.F.loc27_21.1(%u.param: <error>) -> <error> {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %u.ref: <error> = name_ref u, %u
// CHECK:STDOUT: return <error> to %return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.as.J.impl.F.loc24_21.2(%u.param: %i32) -> %i32 [thunk @E.as.J.impl.%E.as.J.impl.F.decl.loc24_21.1] {
// CHECK:STDOUT: fn @E.as.J.impl.F.loc27_21.2(%u.param: %i32) -> %i32 [thunk @E.as.J.impl.%E.as.J.impl.F.decl.loc27_21.1] {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref: %E.as.J.impl.F.type.3487c0.1 = name_ref F, @E.as.J.impl.%E.as.J.impl.F.decl.loc24_21.1 [concrete = constants.%E.as.J.impl.F.1487eb.1]
// CHECK:STDOUT: %F.ref: %E.as.J.impl.F.type.3487c0.1 = name_ref F, @E.as.J.impl.%E.as.J.impl.F.decl.loc27_21.1 [concrete = constants.%E.as.J.impl.F.1487eb.1]
// CHECK:STDOUT: %u.ref: %i32 = name_ref u, %u.param
// CHECK:STDOUT: %return.ref: ref %i32 = name_ref <return slot>, %return.param
// CHECK:STDOUT: %E.as.J.impl.F.call: init <error> = call %F.ref(<error>)
@@ -2486,6 +2544,375 @@ fn F() {
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.7ce
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_call_extend_fn.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete]
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: %J.F.type: type = fn_type @J.F [concrete]
// CHECK:STDOUT: %J.F: %J.F.type = struct_value () [concrete]
// CHECK:STDOUT: %J.assoc_type: type = assoc_entity_type @J [concrete]
// CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, @J.%J.F.decl [concrete]
// CHECK:STDOUT: %E: type = class_type @E [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness @E.%J.impl_witness_table [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type: type = fn_type @E.as.J.impl.F [concrete]
// CHECK:STDOUT: %E.as.J.impl.F: %E.as.J.impl.F.type = struct_value () [concrete]
// CHECK:STDOUT: %J.facet: %J.type = facet_value %E, (%J.impl_witness) [concrete]
// CHECK:STDOUT: %E.G.type: type = fn_type @E.G [concrete]
// CHECK:STDOUT: %E.G: %E.G.type = struct_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .J = %J.decl
// CHECK:STDOUT: .E = %E.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {}
// CHECK:STDOUT: %E.decl: type = class_decl @E [concrete = constants.%E] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @J {
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT: %J.F.decl: %J.F.type = fn_decl @J.F [concrete = constants.%J.F] {} {}
// CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, %J.F.decl [concrete = constants.%assoc0]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: .F = %assoc0
// CHECK:STDOUT: witness = (%J.F.decl)
// CHECK:STDOUT:
// CHECK:STDOUT: !requires:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @E.as.J.impl: %Self.ref as %J.ref {
// CHECK:STDOUT: %E.as.J.impl.F.decl: %E.as.J.impl.F.type = fn_decl @E.as.J.impl.F [concrete = constants.%E.as.J.impl.F] {} {}
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %E.as.J.impl.F.decl
// CHECK:STDOUT: witness = @E.%J.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @E {
// CHECK:STDOUT: impl_decl @E.as.J.impl [concrete] {} {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E]
// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@E.as.J.impl.%E.as.J.impl.F.decl), @E.as.J.impl [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness]
// CHECK:STDOUT: %E.G.decl: %E.G.type = fn_decl @E.G [concrete = constants.%E.G] {} {}
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%E
// CHECK:STDOUT: .J = <poisoned>
// CHECK:STDOUT: .G = %E.G.decl
// CHECK:STDOUT: .F = <poisoned>
// CHECK:STDOUT: extend @E.as.J.impl.%J.ref
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @J.F(@J.%Self: %J.type) {
// CHECK:STDOUT: !definition:
// CHECK:STDOUT:
// CHECK:STDOUT: fn() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.as.J.impl.F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref: %J.assoc_type = name_ref F, @J.%assoc0 [concrete = constants.%assoc0]
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @J.F(constants.%Self) {}
// CHECK:STDOUT:
// CHECK:STDOUT: specific @J.F(constants.%J.facet) {}
// CHECK:STDOUT:
// CHECK:STDOUT: --- call_extend_method.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete]
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic]
// CHECK:STDOUT: %pattern_type.4c4: type = pattern_type %Self.binding.as_type [symbolic]
// CHECK:STDOUT: %J.F.type: type = fn_type @J.F [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %J.F: %J.F.type = struct_value () [concrete]
// CHECK:STDOUT: %J.assoc_type: type = assoc_entity_type @J [concrete]
// CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, @J.%J.F.decl [concrete]
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %Self.binding.as_type [symbolic]
// CHECK:STDOUT: %E: type = class_type @E [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness @E.%J.impl_witness_table [concrete]
// CHECK:STDOUT: %pattern_type.a4a: type = pattern_type %E [concrete]
// CHECK:STDOUT: %E.as.J.impl.F.type: type = fn_type @E.as.J.impl.F [concrete]
// CHECK:STDOUT: %E.as.J.impl.F: %E.as.J.impl.F.type = struct_value () [concrete]
// CHECK:STDOUT: %J.facet: %J.type = facet_value %E, (%J.impl_witness) [concrete]
// CHECK:STDOUT: %E.G.type: type = fn_type @E.G [concrete]
// CHECK:STDOUT: %E.G: %E.G.type = struct_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
// CHECK:STDOUT: %.225: type = fn_type_with_self_type %J.F.type, %J.facet [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .J = %J.decl
// CHECK:STDOUT: .E = %E.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {}
// CHECK:STDOUT: %E.decl: type = class_decl @E [concrete = constants.%E] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @J {
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT: %J.F.decl: %J.F.type = fn_decl @J.F [concrete = constants.%J.F] {
// CHECK:STDOUT: %self.patt: @J.F.%pattern_type (%pattern_type.4c4) = value_binding_pattern self [concrete]
// CHECK:STDOUT: %self.param_patt: @J.F.%pattern_type (%pattern_type.4c4) = value_param_pattern %self.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: @J.F.%Self.binding.as_type (%Self.binding.as_type) = value_param call_param0
// CHECK:STDOUT: %.loc4_14.1: type = splice_block %.loc4_14.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] {
// CHECK:STDOUT: %Self.ref: %J.type = name_ref Self, @J.%Self [symbolic = %Self (constants.%Self)]
// CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self.ref [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
// CHECK:STDOUT: %.loc4_14.2: type = converted %Self.ref, %Self.as_type [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %self: @J.F.%Self.binding.as_type (%Self.binding.as_type) = value_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, %J.F.decl [concrete = constants.%assoc0]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: .F = %assoc0
// CHECK:STDOUT: witness = (%J.F.decl)
// CHECK:STDOUT:
// CHECK:STDOUT: !requires:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @E.as.J.impl: %Self.ref as %J.ref {
// CHECK:STDOUT: %E.as.J.impl.F.decl: %E.as.J.impl.F.type = fn_decl @E.as.J.impl.F [concrete = constants.%E.as.J.impl.F] {
// CHECK:STDOUT: %self.patt: %pattern_type.a4a = value_binding_pattern self [concrete]
// CHECK:STDOUT: %self.param_patt: %pattern_type.a4a = value_param_pattern %self.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: %E = value_param call_param0
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E]
// CHECK:STDOUT: %self: %E = value_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %E.as.J.impl.F.decl
// CHECK:STDOUT: witness = @E.%J.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @E {
// CHECK:STDOUT: impl_decl @E.as.J.impl [concrete] {} {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E]
// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (@E.as.J.impl.%E.as.J.impl.F.decl), @E.as.J.impl [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness]
// CHECK:STDOUT: %E.G.decl: %E.G.type = fn_decl @E.G [concrete = constants.%E.G] {
// CHECK:STDOUT: %self.patt: %pattern_type.a4a = value_binding_pattern self [concrete]
// CHECK:STDOUT: %self.param_patt: %pattern_type.a4a = value_param_pattern %self.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: %E = value_param call_param0
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E]
// CHECK:STDOUT: %self: %E = value_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%E
// CHECK:STDOUT: .J = <poisoned>
// CHECK:STDOUT: .G = %E.G.decl
// CHECK:STDOUT: .F = <poisoned>
// CHECK:STDOUT: extend @E.as.J.impl.%J.ref
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @J.F(@J.%Self: %J.type) {
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)]
// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
// CHECK:STDOUT: %pattern_type: type = pattern_type %Self.binding.as_type [symbolic = %pattern_type (constants.%pattern_type.4c4)]
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %Self.binding.as_type [symbolic = %require_complete (constants.%require_complete)]
// CHECK:STDOUT:
// CHECK:STDOUT: fn(%self.param: @J.F.%Self.binding.as_type (%Self.binding.as_type)) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.as.J.impl.F(%self.param: %E) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.G(%self.param: %E) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %self.ref: %E = name_ref self, %self
// CHECK:STDOUT: %F.ref: %J.assoc_type = name_ref F, @J.%assoc0 [concrete = constants.%assoc0]
// CHECK:STDOUT: %impl.elem0: %.225 = impl_witness_access constants.%J.impl_witness, element0 [concrete = constants.%E.as.J.impl.F]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %self.ref, %impl.elem0
// CHECK:STDOUT: %E.as.J.impl.F.call: init %empty_tuple.type = call %bound_method(%self.ref)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @J.F(constants.%Self) {
// CHECK:STDOUT: %Self => constants.%Self
// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.4c4
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @J.F(constants.%J.facet) {
// CHECK:STDOUT: %Self => constants.%J.facet
// CHECK:STDOUT: %Self.binding.as_type => constants.%E
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.a4a
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_use_extend_constant.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete]
// CHECK:STDOUT: %Self.dc6: %J.type = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: %J.assoc_type: type = assoc_entity_type @J [concrete]
// CHECK:STDOUT: %assoc0.8fd: %J.assoc_type = assoc_entity element0, @J.%X [concrete]
// CHECK:STDOUT: %E: type = class_type @E [concrete]
// CHECK:STDOUT: %.Self: %J.type = symbolic_binding .Self [symbolic_self]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self]
// CHECK:STDOUT: %J.lookup_impl_witness: <witness> = lookup_impl_witness %.Self, @J [symbolic_self]
// CHECK:STDOUT: %impl.elem0: type = impl_witness_access %J.lookup_impl_witness, element0 [symbolic_self]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %J_where.type: type = facet_type <@J where %impl.elem0 = %empty_tuple.type> [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness @E.%J.impl_witness_table [concrete]
// CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete]
// CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete]
// CHECK:STDOUT: %E.G.type: type = fn_type @E.G [concrete]
// CHECK:STDOUT: %E.G: %E.G.type = struct_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .J = %J.decl
// CHECK:STDOUT: .E = %E.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {}
// CHECK:STDOUT: %E.decl: type = class_decl @E [concrete = constants.%E] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @J {
// CHECK:STDOUT: %Self: %J.type = symbolic_binding Self, 0 [symbolic = constants.%Self.dc6]
// CHECK:STDOUT: %X: type = assoc_const_decl @X [concrete] {
// CHECK:STDOUT: %assoc0: %J.assoc_type = assoc_entity element0, @J.%X [concrete = constants.%assoc0.8fd]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: .X = @X.%assoc0
// CHECK:STDOUT: witness = (%X)
// CHECK:STDOUT:
// CHECK:STDOUT: !requires:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic assoc_const @X(@J.%Self: %J.type) {
// CHECK:STDOUT: assoc_const X:! type;
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @E.as.J.impl: %Self.ref as %.loc8_20 {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: witness = @E.%J.impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @E {
// CHECK:STDOUT: impl_decl @E.as.J.impl [concrete] {} {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%E [concrete = constants.%E]
// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type]
// CHECK:STDOUT: %.Self: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self]
// CHECK:STDOUT: %.Self.ref: %J.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self]
// CHECK:STDOUT: %X.ref: %J.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.8fd]
// CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type]
// CHECK:STDOUT: %.loc8_26: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type]
// CHECK:STDOUT: %impl.elem0: type = impl_witness_access constants.%J.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0]
// CHECK:STDOUT: %.loc8_32.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: %.loc8_32.2: type = converted %.loc8_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
// CHECK:STDOUT: %.loc8_20: type = where_expr %.Self [concrete = constants.%J_where.type] {
// CHECK:STDOUT: requirement_base_facet_type constants.%J.type
// CHECK:STDOUT: requirement_rewrite %impl.elem0, %.loc8_32.2
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @E.as.J.impl [concrete]
// CHECK:STDOUT: %J.impl_witness: <witness> = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness]
// CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
// CHECK:STDOUT: %E.G.decl: %E.G.type = fn_decl @E.G [concrete = constants.%E.G] {
// CHECK:STDOUT: %return.patt: <error> = return_slot_pattern [concrete]
// CHECK:STDOUT: %return.param_patt: <error> = out_param_pattern %return.patt, call_param0 [concrete]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %X.ref: %J.assoc_type = name_ref X, @X.%assoc0 [concrete = constants.%assoc0.8fd]
// CHECK:STDOUT: %.loc18_13: type = converted %X.ref, <error> [concrete = <error>]
// CHECK:STDOUT: %return.param: ref <error> = out_param call_param0
// CHECK:STDOUT: %return: ref <error> = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%E
// CHECK:STDOUT: .J = <poisoned>
// CHECK:STDOUT: .X = <poisoned>
// CHECK:STDOUT: .G = %E.G.decl
// CHECK:STDOUT: extend @E.as.J.impl.%.loc8_20
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @E.G() -> <error> {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc18_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple]
// CHECK:STDOUT: return <error> to %return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @X(constants.%Self.dc6) {}
// CHECK:STDOUT:
// CHECK:STDOUT: specific @X(constants.%.Self) {}
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_self_period_associated_type.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {