Skip to content

Commit

Permalink
Do not try to recover from using impl outside class error
Browse files Browse the repository at this point in the history
This prevents crashing when the wrongly used impl uses a poisoned name.
carbon-language#4622
  • Loading branch information
bricknerb committed Jan 3, 2025
1 parent fa9d838 commit 8c572f4
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 46 deletions.
1 change: 1 addition & 0 deletions toolchain/check/handle_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ 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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,10 @@ library "[[@TEST_NAME]]";
class C {}

namespace N;
// CHECK:STDERR: fail_alias.carbon:[[@LINE+3]]:9: note: declared here [NameUseBeforeDeclNote]
// CHECK:STDERR: fail_alias.carbon:[[@LINE+4]]:9: note: declared here [NameUseBeforeDeclNote]
// CHECK:STDERR: alias N.C = C;
// CHECK:STDERR: ^
// CHECK:STDERR:
alias N.C = C;

// --- ignored_poison_in_import.carbon
Expand All @@ -259,6 +260,37 @@ impl library "[[@TEST_NAME]]";
// TODO: This should fail since N.C was poisoned in the api.
class N.C {}

// --- using_poisoned_name_in_impl.carbon

library "[[@TEST_NAME]]";

interface C {};

namespace N;
// Here we use C and poison N.C.
fn N.F1(x: C);

class N.X {
extend impl as C {
}
}

// --- fail_using_poisoned_name_in_impl_outside_class.carbon

library "[[@TEST_NAME]]";

interface A {
fn B();
}
class X {
extend impl as A {
// CHECK:STDERR: fail_using_poisoned_name_in_impl_outside_class.carbon:[[@LINE+3]]:29: error: `impl as` can only be used in a class [ImplAsOutsideClass]
// CHECK:STDERR: fn F() { return; } impl as B {}
// CHECK:STDERR: ^~
fn F() { return; } impl as B {}
}
}

// CHECK:STDOUT: --- no_poison.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
Expand Down Expand Up @@ -923,7 +955,7 @@ class N.C {}
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {}
// CHECK:STDOUT: %N: <namespace> = namespace [template] {}
// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C]
// CHECK:STDOUT: %.loc11: type = bind_alias <invalid>, %C.decl [template = constants.%C]
// CHECK:STDOUT: %.loc12: type = bind_alias <invalid>, %C.decl [template = constants.%C]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
Expand Down Expand Up @@ -1003,3 +1035,109 @@ class N.C {}
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- using_poisoned_name_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C.type: type = facet_type <@C> [template]
// CHECK:STDOUT: %Self: %C.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template]
// CHECK:STDOUT: %F1: %F1.type = struct_value () [template]
// CHECK:STDOUT: %X: type = class_type @X [template]
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: .N = %N
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = interface_decl @C [template = constants.%C.type] {} {}
// CHECK:STDOUT: %N: <namespace> = namespace [template] {
// CHECK:STDOUT: .F1 = %F1.decl
// CHECK:STDOUT: .X = %X.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] {
// CHECK:STDOUT: %x.patt: %C.type = binding_pattern x
// CHECK:STDOUT: %x.param_patt: %C.type = value_param_pattern %x.patt, runtime_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %x.param: %C.type = value_param runtime_param0
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C.type]
// CHECK:STDOUT: %x: %C.type = bind_name x, %x.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %X.decl: type = class_decl @X [template = constants.%X] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @C {
// CHECK:STDOUT: %Self: %C.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 %C.ref {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: witness = @X.%impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @X {
// CHECK:STDOUT: impl_decl @impl [template] {} {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%X [template = constants.%X]
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness () [template = constants.%impl_witness]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [template = constants.%complete_type]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%X
// CHECK:STDOUT: extend @impl.%C.ref
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: }
// 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> [template]
// CHECK:STDOUT: %Self: %A.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %B.type: type = fn_type @B [template]
// CHECK:STDOUT: %B: %B.type = struct_value () [template]
// CHECK:STDOUT: %B.assoc_type: type = assoc_entity_type %A.type, %B.type [template]
// CHECK:STDOUT: %assoc0: %B.assoc_type = assoc_entity element0, @A.%B.decl [template]
// CHECK:STDOUT: %X: type = class_type @X [template]
// 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 [template = constants.%B] {} {}
// CHECK:STDOUT: %assoc0: %B.assoc_type = assoc_entity element0, %B.decl [template = 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: <unexpected>.inst26.loc8_15 as <unexpected>.inst27.loc8_18;
// CHECK:STDOUT:
// CHECK:STDOUT: class @X {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%X
// CHECK:STDOUT: extend <unexpected>.inst27.loc8_18
// CHECK:STDOUT: complete_type_witness = invalid
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @B(@A.%Self: %A.type) {
// CHECK:STDOUT:
// CHECK:STDOUT: fn();
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F();
// CHECK:STDOUT:
// CHECK:STDOUT: specific @B(constants.%Self) {}
// CHECK:STDOUT:
51 changes: 7 additions & 44 deletions toolchain/check/testdata/impl/fail_impl_as_scope.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,17 @@ impl as Simple {
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Simple.type: type = facet_type <@Simple> [template]
// CHECK:STDOUT: %Self: %Simple.type = bind_symbolic_name Self, 0 [symbolic]
// CHECK:STDOUT: %F.type.1: type = fn_type @F.1 [template]
// CHECK:STDOUT: %F.1: %F.type.1 = struct_value () [template]
// CHECK:STDOUT: %F.assoc_type: type = assoc_entity_type %Simple.type, %F.type.1 [template]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: %F.assoc_type: type = assoc_entity_type %Simple.type, %F.type [template]
// CHECK:STDOUT: %assoc0: %F.assoc_type = assoc_entity element0, @Simple.%F.decl [template]
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness (@impl.%F.decl) [template]
// CHECK:STDOUT: %F.type.2: type = fn_type @F.2 [template]
// CHECK:STDOUT: %F.2: %F.type.2 = struct_value () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// 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 [template = constants.%Simple.type] {} {}
// CHECK:STDOUT: impl_decl @impl [template] {} {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, <error> [template = <error>]
// CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [template = constants.%Simple.type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl_witness: <witness> = impl_witness (@impl.%F.decl) [template = constants.%impl_witness]
// CHECK:STDOUT: }
// CHECK:STDOUT: file {}
// 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.1 = fn_decl @F.1 [template = constants.%F.1] {} {}
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {}
// CHECK:STDOUT: %assoc0: %F.assoc_type = assoc_entity element0, %F.decl [template = constants.%assoc0]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
Expand All @@ -65,25 +43,10 @@ impl as Simple {
// CHECK:STDOUT: witness = (%F.decl)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: %Self.ref as %Simple.ref {
// CHECK:STDOUT: %F.decl: %F.type.2 = fn_decl @F.2 [template = constants.%F.2] {} {}
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F.decl
// CHECK:STDOUT: witness = file.%impl_witness
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @F.1(@Simple.%Self: %Simple.type) {
// CHECK:STDOUT: generic fn @F(@Simple.%Self: %Simple.type) {
// CHECK:STDOUT:
// CHECK:STDOUT: fn();
// CHECK:STDOUT: }
// CHECK:STDOUT:
// 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: specific @F.1(<error>) {}
// CHECK:STDOUT: specific @F(constants.%Self) {}
// CHECK:STDOUT:

0 comments on commit 8c572f4

Please sign in to comment.