From d5f3d3365aef4e392b95e73e6b664ab7395978e4 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Fri, 14 Feb 2025 15:02:10 -0500 Subject: [PATCH] Allow checking to continue after 'impl as' outside class (#4937) Currently it returns false which just ends typechecking. Instead handle the error state later and avoid firing overlapping diagnostics in 'extend impl as'. --------- Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com> --- toolchain/check/handle_impl.cpp | 8 +- toolchain/check/impl.cpp | 5 + toolchain/check/impl_lookup.cpp | 17 +- .../no_prelude/name_poisoning.carbon | 62 --- .../testdata/impl/fail_impl_as_scope.carbon | 99 +++- .../no_prelude/fail_extend_impl_scope.carbon | 139 +++++- .../impl/no_prelude/fail_impl_as_scope.carbon | 444 ++++++++++++++++++ .../impl/no_prelude/fail_impl_bad_type.carbon | 4 +- .../todo_impl_with_unrelated_fn.carbon | 105 +++++ 9 files changed, 776 insertions(+), 107 deletions(-) create mode 100644 toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon create mode 100644 toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 8b17c621af4f..80856d073b3b 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -99,7 +99,6 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id) "`impl as` can only be used in a class"); context.emitter().Emit(node_id, ImplAsOutsideClass); self_type_id = SemIR::ErrorInst::SingletonTypeId; - return false; } // Build the implicit access to the enclosing `Self`. @@ -141,14 +140,14 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, DiagnoseExtendImplOutsideClass(context, node_id); return false; } - auto& parent_scope = context.name_scopes().Get(parent_scope_id); - // TODO: This is also valid in a mixin. if (!TryAsClassScope(context, parent_scope_id)) { DiagnoseExtendImplOutsideClass(context, node_id); return false; } + auto& parent_scope = context.name_scopes().Get(parent_scope_id); + if (params_node.has_value()) { CARBON_DIAGNOSTIC(ExtendImplForall, Error, "cannot `extend` a parameterized `impl`"); @@ -387,7 +386,8 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, ReplaceInstBeforeConstantUse(context, impl_decl_id, impl_decl); // For an `extend impl` declaration, mark the impl as extending this `impl`. - if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { + if (self_type_id != SemIR::ErrorInst::SingletonTypeId && + introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto extend_node = introducer.modifier_node_id(ModifierOrder::Decl); if (impl_info.generic_id.has_value()) { SemIR::TypeId type_id = context.insts().Get(constraint_inst_id).type_id(); diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 2d2cd033a3f6..7fd2346fdb85 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -86,6 +86,11 @@ auto ImplWitnessForDeclaration(Context& context, const SemIR::Impl& impl) -> SemIR::InstId { CARBON_CHECK(!impl.has_definition_started()); + auto self_type_id = context.types().GetTypeIdForTypeInstId(impl.self_id); + if (self_type_id == SemIR::ErrorInst::SingletonTypeId) { + // When 'impl as' is invalid, the self type is an error. + return SemIR::ErrorInst::SingletonInstId; + } auto facet_type_id = context.types().GetTypeIdForTypeInstId(impl.constraint_id); if (facet_type_id == SemIR::ErrorInst::SingletonTypeId) { diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 77a906c5a037..cef737797e85 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -145,14 +145,11 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id, // constraint, the unique `interface` declaration.) auto specific_id = SemIR::SpecificId::None; - // This check comes first to avoid deduction when the witness_id is missing. - // We use an error value to indicate an error during creation of the impl, - // such as a recursive impl which will cause deduction to recurse - // infinitely. - if (!impl.witness_id.has_value() || - impl.witness_id == SemIR::ErrorInst::SingletonInstId) { - // TODO: Diagnose if the impl isn't defined yet? - return SemIR::InstId::None; + // This check comes first to avoid deduction with an invalid impl. We use an + // error value to indicate an error during creation of the impl, such as a + // recursive impl which will cause deduction to recurse infinitely. + if (impl.witness_id == SemIR::ErrorInst::SingletonInstId) { + continue; } if (impl.generic_id.has_value()) { specific_id = DeduceImplArguments(context, loc_id, impl, type_const_id, @@ -175,6 +172,10 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id, // the constraint's interfaces. continue; } + if (!impl.witness_id.has_value()) { + // TODO: Diagnose if the impl isn't defined yet? + return SemIR::InstId::None; + } LoadImportRef(context, impl.witness_id); if (specific_id.has_value()) { // We need a definition of the specific `impl` so we can access its diff --git a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon index a0759c9f53a2..d014bb5d8cbb 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon @@ -301,24 +301,6 @@ class N.X { } } -// --- fail_using_poisoned_name_in_impl_outside_class.carbon - -library "[[@TEST_NAME]]"; - -interface A { - fn B(); -} -class X { - extend impl as A { - fn F() { return; } - // CHECK:STDERR: fail_using_poisoned_name_in_impl_outside_class.carbon:[[@LINE+4]]:10: error: `impl as` can only be used in a class [ImplAsOutsideClass] - // CHECK:STDERR: impl as B {} - // CHECK:STDERR: ^~ - // CHECK:STDERR: - impl as B {} - } -} - // --- fail_poison_when_lookup_fails.carbon library "[[@TEST_NAME]]"; @@ -1200,50 +1182,6 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: fn @F1(%x.param_patt: %C.type); // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_using_poisoned_name_in_impl_outside_class.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic] -// CHECK:STDOUT: %B.type: type = fn_type @B [concrete] -// CHECK:STDOUT: %B: %B.type = struct_value () [concrete] -// CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type %A.type [concrete] -// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.%B.decl [concrete] -// CHECK:STDOUT: %X: type = class_type @X [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file {} -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A { -// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} -// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, %B.decl [concrete = constants.%assoc0] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .B = %assoc0 -// CHECK:STDOUT: witness = (%B.decl) -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: .inst26.loc8_15 as .inst27.loc8_18; -// CHECK:STDOUT: -// CHECK:STDOUT: class @X { -// CHECK:STDOUT: complete_type_witness = invalid -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%X -// CHECK:STDOUT: .A = -// CHECK:STDOUT: extend .inst27.loc8_18 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic fn @B(@A.%Self: %A.type) { -// CHECK:STDOUT: fn(); -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); -// CHECK:STDOUT: -// CHECK:STDOUT: specific @B(constants.%Self) {} -// CHECK:STDOUT: // CHECK:STDOUT: --- fail_poison_when_lookup_fails.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon index 1855c990024b..42675db574f1 100644 --- a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon +++ b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon @@ -47,17 +47,37 @@ fn Function() { // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic] -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type.e2e: type = fn_type @F.1 [concrete] +// CHECK:STDOUT: %F.df8: %F.type.e2e = struct_value () [concrete] // CHECK:STDOUT: %Simple.assoc_type: type = assoc_entity_type %Simple.type [concrete] // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, @Simple.%F.decl [concrete] +// CHECK:STDOUT: %F.type.fe5: type = fn_type @F.2 [concrete] +// CHECK:STDOUT: %F.32d: %F.type.fe5 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Simple = %Simple.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [concrete = constants.%Simple.type] {} {} +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type.e2e = fn_decl @F.1 [concrete = constants.%F.df8] {} {} // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, %F.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -66,28 +86,61 @@ fn Function() { // CHECK:STDOUT: witness = (%F.decl) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic fn @F(@Simple.%Self: %Simple.type) { +// CHECK:STDOUT: impl @impl: %Self.ref as %Simple.ref { +// CHECK:STDOUT: %F.decl: %F.type.fe5 = fn_decl @F.2 [concrete = constants.%F.32d] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F.1(@Simple.%Self: %Simple.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @F(constants.%Self) {} +// CHECK:STDOUT: fn @F.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F.1(constants.%Self) {} // CHECK:STDOUT: // CHECK:STDOUT: --- fail_function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [concrete] // CHECK:STDOUT: %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic] -// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] -// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type.e2e: type = fn_type @F.1 [concrete] +// CHECK:STDOUT: %F.df8: %F.type.e2e = struct_value () [concrete] // CHECK:STDOUT: %Simple.assoc_type: type = assoc_entity_type %Simple.type [concrete] // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, @Simple.%F.decl [concrete] +// CHECK:STDOUT: %Function.type: type = fn_type @Function [concrete] +// CHECK:STDOUT: %Function: %Function.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type.bc7: type = fn_type @F.2 [concrete] +// CHECK:STDOUT: %F.0a2: %F.type.bc7 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Simple = %Simple.decl +// CHECK:STDOUT: .Function = %Function.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Simple.decl: type = interface_decl @Simple [concrete = constants.%Simple.type] {} {} +// CHECK:STDOUT: %Function.decl: %Function.type = fn_decl @Function [concrete = constants.%Function] {} {} +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Simple { // CHECK:STDOUT: %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type.e2e = fn_decl @F.1 [concrete = constants.%F.df8] {} {} // CHECK:STDOUT: %assoc0: %Simple.assoc_type = assoc_entity element0, %F.decl [concrete = constants.%assoc0] // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -96,11 +149,31 @@ fn Function() { // CHECK:STDOUT: witness = (%F.decl) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic fn @F(@Simple.%Self: %Simple.type) { +// CHECK:STDOUT: impl @impl: %Self.ref as %Simple.ref { +// CHECK:STDOUT: %F.decl: %F.type.bc7 = fn_decl @F.2 [concrete = constants.%F.0a2] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F.1(@Simple.%Self: %Simple.type) { // CHECK:STDOUT: fn(); // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @Function(); +// CHECK:STDOUT: fn @Function() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @F(constants.%Self) {} +// CHECK:STDOUT: fn @F.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F.1(constants.%Self) {} // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon b/toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon index 23c5a58ba75d..6e68bfdb787e 100644 --- a/toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon +++ b/toolchain/check/testdata/impl/no_prelude/fail_extend_impl_scope.carbon @@ -36,24 +36,20 @@ fn F() { library "[[@TEST_NAME]]"; interface Z { - fn Zero() -> Self; + fn Zero(); // CHECK:STDERR: fail_extend_impl_self_interface.carbon:[[@LINE+4]]:15: error: `impl as` can only be used in a class [ImplAsOutsideClass] // CHECK:STDERR: extend impl as Z { // CHECK:STDERR: ^~ // CHECK:STDERR: extend impl as Z { - fn Zero() -> Self { - return {}; - } + fn Zero() {} } } class Point { extend impl as Z { - fn Zero() -> Self { - return {}; - } + fn Zero() {} } } @@ -148,27 +144,136 @@ fn F() { // CHECK:STDOUT: constants { // CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] // CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic] -// CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self [symbolic] +// CHECK:STDOUT: %Zero.type.822: type = fn_type @Zero.1 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Zero.d14: %Zero.type.822 = struct_value () [concrete] +// CHECK:STDOUT: %Z.assoc_type: type = assoc_entity_type %Z.type [concrete] +// CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, @Z.%Zero.decl [concrete] +// CHECK:STDOUT: %Zero.type.db4: type = fn_type @Zero.2, @impl.1(%Self) [symbolic] +// CHECK:STDOUT: %Zero.8fb: %Zero.type.db4 = struct_value () [symbolic] +// CHECK:STDOUT: %Point: type = class_type @Point [concrete] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.2.%Zero.decl) [concrete] +// CHECK:STDOUT: %Zero.type.e33: type = fn_type @Zero.3 [concrete] +// CHECK:STDOUT: %Zero.dec: %Zero.type.e33 = struct_value () [concrete] +// CHECK:STDOUT: %Z.facet: %Z.type = facet_value %Point, %impl_witness [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %Point.val: %Point = struct_value () [concrete] +// CHECK:STDOUT: %.c37: type = fn_type_with_self_type %Zero.type.822, %Z.facet [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Z = %Z.decl +// CHECK:STDOUT: .Point = %Point.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} +// CHECK:STDOUT: %Point.decl: type = class_decl @Point [concrete = constants.%Point] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @Z { +// CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %Zero.decl: %Zero.type.822 = fn_decl @Zero.1 [concrete = constants.%Zero.d14] {} {} +// CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, %Zero.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: impl_decl @impl.1 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = .inst15 -// CHECK:STDOUT: .Zero = .inst32.loc4_20 -// CHECK:STDOUT: witness = invalid +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Zero = %assoc0 +// CHECK:STDOUT: .Z = +// CHECK:STDOUT: witness = (%Zero.decl) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic fn @Zero(.inst15: %Z.type) { +// CHECK:STDOUT: generic impl @impl.1(@Z.%Self: %Z.type) { +// CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self [symbolic = %Self.as_type (constants.%Self.as_type)] +// CHECK:STDOUT: %Zero.type: type = fn_type @Zero.2, @impl.1(%Self) [symbolic = %Zero.type (constants.%Zero.type.db4)] +// CHECK:STDOUT: %Zero: @impl.1.%Zero.type (%Zero.type.db4) = struct_value () [symbolic = %Zero (constants.%Zero.8fb)] // CHECK:STDOUT: -// CHECK:STDOUT: fn() -> @Zero.%Self.as_type (%Self.as_type); +// CHECK:STDOUT: impl: %Self.ref as %Z.ref { +// CHECK:STDOUT: %Zero.decl: @impl.1.%Zero.type (%Zero.type.db4) = fn_decl @Zero.2 [symbolic = @impl.1.%Zero (constants.%Zero.8fb)] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Zero = %Zero.decl +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @Zero(constants.%Self) { +// CHECK:STDOUT: impl @impl.2: %Self.ref as %Z.ref { +// CHECK:STDOUT: %Zero.decl: %Zero.type.e33 = fn_decl @Zero.3 [concrete = constants.%Zero.dec] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Zero = %Zero.decl +// CHECK:STDOUT: witness = @Point.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Point { +// CHECK:STDOUT: impl_decl @impl.2 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Point [concrete = constants.%Point] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.2.%Zero.decl) [concrete = constants.%impl_witness] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Point +// CHECK:STDOUT: .Z = +// CHECK:STDOUT: .Zero = +// CHECK:STDOUT: extend @impl.2.%Z.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Zero.1(@Z.%Self: %Z.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Zero.2(@Z.%Self: %Z.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Zero.3() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc25_5.1: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %Point.ref: type = name_ref Point, file.%Point.decl [concrete = constants.%Point] +// CHECK:STDOUT: %.loc25_5.2: ref %Point = temporary_storage +// CHECK:STDOUT: %.loc25_5.3: init %Point = class_init (), %.loc25_5.2 [concrete = constants.%Point.val] +// CHECK:STDOUT: %.loc25_5.4: ref %Point = temporary %.loc25_5.2, %.loc25_5.3 +// CHECK:STDOUT: %.loc25_7: ref %Point = converted %.loc25_5.1, %.loc25_5.4 +// CHECK:STDOUT: %Zero.ref: %Z.assoc_type = name_ref Zero, @Z.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %impl.elem0: %.c37 = impl_witness_access constants.%impl_witness, element0 [concrete = constants.%Zero.dec] +// CHECK:STDOUT: %Zero.call: init %empty_tuple.type = call %impl.elem0() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.1(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @impl.1(constants.%Self) { +// CHECK:STDOUT: !definition: // CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %Self.as_type => constants.%Self.as_type +// CHECK:STDOUT: %Zero.type => constants.%Zero.type.db4 +// CHECK:STDOUT: %Zero => constants.%Zero.8fb // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.2(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @impl.1(%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.1(constants.%Z.facet) {} +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon b/toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon new file mode 100644 index 000000000000..9fb43cf3d2ba --- /dev/null +++ b/toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon @@ -0,0 +1,444 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/fail_impl_as_scope.carbon + +// --- fail_impl_as_file_scope.carbon +library "[[@TEST_NAME]]"; + +interface I {} + +fn G() {} + +// CHECK:STDERR: fail_impl_as_file_scope.carbon:[[@LINE+4]]:6: error: `impl as` can only be used in a class [ImplAsOutsideClass] +// CHECK:STDERR: impl as I {} +// CHECK:STDERR: ^~ +// CHECK:STDERR: +impl as I {} + +fn F() { G(); } + +// --- fail_impl_as_function_scope.carbon +library "[[@TEST_NAME]]"; + +interface J {} + +fn G() {} + +fn F() { + // CHECK:STDERR: fail_impl_as_function_scope.carbon:[[@LINE+4]]:8: error: `impl as` can only be used in a class [ImplAsOutsideClass] + // CHECK:STDERR: impl as J {} + // CHECK:STDERR: ^~ + // CHECK:STDERR: + impl as J {} + G(); +} + +// --- fail_impl_as_self_interface.carbon +library "[[@TEST_NAME]]"; + +interface Z { + fn Zero(); + + // CHECK:STDERR: fail_impl_as_self_interface.carbon:[[@LINE+4]]:9: error: `impl as` can only be used in a class [ImplAsOutsideClass] + // CHECK:STDERR: impl as Z { + // CHECK:STDERR: ^~ + // CHECK:STDERR: + impl as Z { + fn Zero() {} + } +} + +class Point { + impl as Z { + fn Zero() {} + } +} + +fn F() { + // Even if the `impl` is diagnosed above, we must not add the impl of the + // interface to itself in a way that allows it to be used during impl lookup, + // or we end up with infinite impl lookup recursion here. + ({} as Point).(Z.Zero)(); +} + +// --- fail_impl_as_other_interface.carbon + +library "[[@TEST_NAME]]"; + +interface A { + fn B(); +} +interface C {} + +fn G() {} + +class X { + impl as A { + // CHECK:STDERR: fail_impl_as_other_interface.carbon:[[@LINE+4]]:10: error: `impl as` can only be used in a class [ImplAsOutsideClass] + // CHECK:STDERR: impl as C {} + // CHECK:STDERR: ^~ + // CHECK:STDERR: + impl as C {} + fn B() { + G(); + } + } +} + +// CHECK:STDOUT: --- fail_impl_as_file_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %Self.ref as %I.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] +// CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_impl_as_function_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] +// CHECK:STDOUT: %Self: %J.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .J = %J.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @J { +// CHECK:STDOUT: %Self: %J.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %Self.ref as %J.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] +// CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_impl_as_self_interface.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] +// CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %Zero.type.822: type = fn_type @Zero.1 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %Zero.d14: %Zero.type.822 = struct_value () [concrete] +// CHECK:STDOUT: %Z.assoc_type: type = assoc_entity_type %Z.type [concrete] +// CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, @Z.%Zero.decl [concrete] +// CHECK:STDOUT: %Zero.type.db4: type = fn_type @Zero.2, @impl.1(%Self) [symbolic] +// CHECK:STDOUT: %Zero.8fb: %Zero.type.db4 = struct_value () [symbolic] +// CHECK:STDOUT: %Point: type = class_type @Point [concrete] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.2.%Zero.decl) [concrete] +// CHECK:STDOUT: %Zero.type.e33: type = fn_type @Zero.3 [concrete] +// CHECK:STDOUT: %Zero.dec: %Zero.type.e33 = struct_value () [concrete] +// CHECK:STDOUT: %Z.facet: %Z.type = facet_value %Point, %impl_witness [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %Point.val: %Point = struct_value () [concrete] +// CHECK:STDOUT: %.c37: type = fn_type_with_self_type %Zero.type.822, %Z.facet [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Z = %Z.decl +// CHECK:STDOUT: .Point = %Point.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} +// CHECK:STDOUT: %Point.decl: type = class_decl @Point [concrete = constants.%Point] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Z { +// CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %Zero.decl: %Zero.type.822 = fn_decl @Zero.1 [concrete = constants.%Zero.d14] {} {} +// CHECK:STDOUT: %assoc0: %Z.assoc_type = assoc_entity element0, %Zero.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: impl_decl @impl.1 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Zero = %assoc0 +// CHECK:STDOUT: .Z = +// CHECK:STDOUT: witness = (%Zero.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic impl @impl.1(@Z.%Self: %Z.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Zero.type: type = fn_type @Zero.2, @impl.1(%Self) [symbolic = %Zero.type (constants.%Zero.type.db4)] +// CHECK:STDOUT: %Zero: @impl.1.%Zero.type (%Zero.type.db4) = struct_value () [symbolic = %Zero (constants.%Zero.8fb)] +// CHECK:STDOUT: +// CHECK:STDOUT: impl: %Self.ref as %Z.ref { +// CHECK:STDOUT: %Zero.decl: @impl.1.%Zero.type (%Zero.type.db4) = fn_decl @Zero.2 [symbolic = @impl.1.%Zero (constants.%Zero.8fb)] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Zero = %Zero.decl +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.2: %Self.ref as %Z.ref { +// CHECK:STDOUT: %Zero.decl: %Zero.type.e33 = fn_decl @Zero.3 [concrete = constants.%Zero.dec] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Zero = %Zero.decl +// CHECK:STDOUT: witness = @Point.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Point { +// CHECK:STDOUT: impl_decl @impl.2 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Point [concrete = constants.%Point] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.2.%Zero.decl) [concrete = constants.%impl_witness] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Point +// CHECK:STDOUT: .Z = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Zero.1(@Z.%Self: %Z.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @Zero.2(@Z.%Self: %Z.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: fn() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Zero.3() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc25_5.1: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %Point.ref: type = name_ref Point, file.%Point.decl [concrete = constants.%Point] +// CHECK:STDOUT: %.loc25_5.2: ref %Point = temporary_storage +// CHECK:STDOUT: %.loc25_5.3: init %Point = class_init (), %.loc25_5.2 [concrete = constants.%Point.val] +// CHECK:STDOUT: %.loc25_5.4: ref %Point = temporary %.loc25_5.2, %.loc25_5.3 +// CHECK:STDOUT: %.loc25_7: ref %Point = converted %.loc25_5.1, %.loc25_5.4 +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: %Zero.ref: %Z.assoc_type = name_ref Zero, @Z.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %impl.elem0: %.c37 = impl_witness_access constants.%impl_witness, element0 [concrete = constants.%Zero.dec] +// CHECK:STDOUT: %Zero.call: init %empty_tuple.type = call %impl.elem0() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.1(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @impl.1(constants.%Self) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self => constants.%Self +// CHECK:STDOUT: %Zero.type => constants.%Zero.type.db4 +// CHECK:STDOUT: %Zero => constants.%Zero.8fb +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.2(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @impl.1(%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Zero.1(constants.%Z.facet) {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_impl_as_other_interface.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %Self.31d: %A.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %B.type.1c3: type = fn_type @B.1 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %B.b08: %B.type.1c3 = struct_value () [concrete] +// CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type %A.type [concrete] +// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.%B.decl [concrete] +// CHECK:STDOUT: %C.type: type = facet_type <@C> [concrete] +// CHECK:STDOUT: %Self.02e: %C.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.1.%B.decl) [concrete] +// CHECK:STDOUT: %B.type.d47: type = fn_type @B.2 [concrete] +// CHECK:STDOUT: %B.4af: %B.type.d47 = struct_value () [concrete] +// CHECK:STDOUT: %A.facet: %A.type = facet_value %X, %impl_witness [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: .X = %X.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %C.decl: type = interface_decl @C [concrete = constants.%C.type] {} {} +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.31d] +// CHECK:STDOUT: %B.decl: %B.type.1c3 = fn_decl @B.1 [concrete = constants.%B.b08] {} {} +// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, %B.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .B = %assoc0 +// CHECK:STDOUT: witness = (%B.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @C { +// CHECK:STDOUT: %Self: %C.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.02e] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.1: %Self.ref as %A.ref { +// CHECK:STDOUT: impl_decl @impl.2 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, [concrete = ] +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: %B.type.d47 = fn_decl @B.2 [concrete = constants.%B.4af] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .C = +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .G = +// CHECK:STDOUT: witness = @X.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.2: %Self.ref as %C.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @X { +// CHECK:STDOUT: impl_decl @impl.1 [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X] +// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.1.%B.decl) [concrete = constants.%impl_witness] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%X +// CHECK:STDOUT: .A = +// CHECK:STDOUT: .C = +// CHECK:STDOUT: .G = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @B.1(@A.%Self: %A.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] +// CHECK:STDOUT: %G.call: init %empty_tuple.type = call %G.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.1(constants.%Self.31d) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.1(constants.%A.facet) {} +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/fail_impl_bad_type.carbon b/toolchain/check/testdata/impl/no_prelude/fail_impl_bad_type.carbon index e5e65646d558..f1fe7bf97b18 100644 --- a/toolchain/check/testdata/impl/no_prelude/fail_impl_bad_type.carbon +++ b/toolchain/check/testdata/impl/no_prelude/fail_impl_bad_type.carbon @@ -22,7 +22,6 @@ impl true as I {} // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] -// CHECK:STDOUT: %impl_witness: = impl_witness () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -35,7 +34,6 @@ impl true as I {} // CHECK:STDOUT: %.loc17: type = converted %true, [concrete = ] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %impl_witness: = impl_witness () [concrete = constants.%impl_witness] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { @@ -48,6 +46,6 @@ impl true as I {} // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: as %I.ref { // CHECK:STDOUT: !members: -// CHECK:STDOUT: witness = file.%impl_witness +// CHECK:STDOUT: witness = // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon b/toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon new file mode 100644 index 000000000000..f92a8c90fadb --- /dev/null +++ b/toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon @@ -0,0 +1,105 @@ +// 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 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/todo_impl_with_unrelated_fn.carbon + +interface A { + fn B(); +} + +class X { + impl as A { + fn B() {} + // TODO: This should be diagnosed as an error; there is no `A.F` to + // implement here. + fn F() {} + } +} + +// CHECK:STDOUT: --- todo_impl_with_unrelated_fn.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %B.type.1c3: type = fn_type @B.1 [concrete] +// CHECK:STDOUT: %B.b08: %B.type.1c3 = struct_value () [concrete] +// CHECK:STDOUT: %A.assoc_type: type = assoc_entity_type %A.type [concrete] +// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, @A.%B.decl [concrete] +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%B.decl) [concrete] +// CHECK:STDOUT: %B.type.d47: type = fn_type @B.2 [concrete] +// CHECK:STDOUT: %B.4af: %B.type.d47 = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %A.facet: %A.type = facet_value %X, %impl_witness [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .X = %X.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %B.decl: %B.type.1c3 = fn_decl @B.1 [concrete = constants.%B.b08] {} {} +// CHECK:STDOUT: %assoc0: %A.assoc_type = assoc_entity element0, %B.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .B = %assoc0 +// CHECK:STDOUT: witness = (%B.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %Self.ref as %A.ref { +// CHECK:STDOUT: %B.decl: %B.type.d47 = fn_decl @B.2 [concrete = constants.%B.4af] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: witness = @X.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @X { +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [concrete = constants.%X] +// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%B.decl) [concrete = constants.%impl_witness] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%X +// CHECK:STDOUT: .A = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @B.1(@A.%Self: %A.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.1(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.1(constants.%A.facet) {} +// CHECK:STDOUT: