Skip to content

Commit

Permalink
Disallow abstract or base on class declarations (that are not def…
Browse files Browse the repository at this point in the history
  • Loading branch information
dwblaikie authored Oct 10, 2024
1 parent 1338f9e commit b1014bf
Show file tree
Hide file tree
Showing 8 changed files with 1,178 additions and 935 deletions.
22 changes: 7 additions & 15 deletions toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,6 @@ static auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
return false;
}

// The introducer kind must match the previous declaration.
// TODO: The rule here is not yet decided. See #3384.
if (prev_class.inheritance_kind != new_class.inheritance_kind) {
CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducer, Error,
"class redeclared with different inheritance kind");
CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducerPrevious, Note,
"previously declared here");
context.emitter()
.Build(new_loc, ClassRedeclarationDifferentIntroducer)
.Note(prev_loc, ClassRedeclarationDifferentIntroducerPrevious)
.Emit();
}

if (new_is_definition) {
prev_class.MergeDefinition(new_class);
prev_class.scope_id = new_class.scope_id;
Expand Down Expand Up @@ -194,9 +181,14 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
auto introducer =
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Class>();
CheckAccessModifiersOnDecl(context, introducer, parent_scope_inst);
auto always_acceptable_modifiers =
KeywordModifierSet::Access | KeywordModifierSet::Extern;
LimitModifiersOnDecl(context, introducer,
KeywordModifierSet::Class | KeywordModifierSet::Access |
KeywordModifierSet::Extern);
always_acceptable_modifiers | KeywordModifierSet::Class);
if (!is_definition) {
LimitModifiersOnNotDefinition(context, introducer,
always_acceptable_modifiers);
}
RestrictExternModifierOnDecl(context, introducer, parent_scope_inst,
is_definition);

Expand Down
6 changes: 6 additions & 0 deletions toolchain/check/modifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ inline auto LimitModifiersOnDecl(Context& context,
ForbidModifiersOnDecl(context, introducer, ~allowed, "");
}

inline auto LimitModifiersOnNotDefinition(Context& context,
DeclIntroducerState& introducer,
KeywordModifierSet allowed) -> void {
ForbidModifiersOnDecl(context, introducer, ~allowed, ", only definition");
}

// Restricts the `extern` modifier to only be used on namespace-scoped
// declarations. Diagnoses and cleans up:
// - `extern library` on a definition.
Expand Down
186 changes: 126 additions & 60 deletions toolchain/check/testdata/class/fail_abstract.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_abstract.carbon


// --- fail_todo_rejects_valid_abstract_subobject_construction.carbon
abstract class Abstract {
var a: i32;
}
Expand All @@ -20,17 +22,34 @@ class Derived {

fn Make() -> Derived {
// TODO: This should be valid, and should construct an instance of `partial Abstract` as the base.
// CHECK:STDERR: fail_abstract.carbon:[[@LINE+3]]:19: error: cannot construct instance of abstract class; consider using `partial Abstract` instead
// CHECK:STDERR: fail_todo_rejects_valid_abstract_subobject_construction.carbon:[[@LINE+6]]:19: error: cannot construct instance of abstract class; consider using `partial Abstract` instead
// CHECK:STDERR: return {.base = {.a = 1}, .d = 7};
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_rejects_valid_abstract_subobject_access.carbon: error: `Main//default` previously provided by `fail_todo_rejects_valid_abstract_subobject_construction.carbon`
// CHECK:STDERR:
return {.base = {.a = 1}, .d = 7};
}

// --- fail_todo_rejects_valid_abstract_subobject_access.carbon

// CHECK:STDERR: fail_todo_rejects_valid_abstract_subobject_access.carbon:[[@LINE+6]]:14: error: name `Derived` not found
// CHECK:STDERR: fn Access(d: Derived) -> (i32, i32) {
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_abstract_decl.carbon: error: `Main//default` previously provided by `fail_todo_rejects_valid_abstract_subobject_construction.carbon`
// CHECK:STDERR:
fn Access(d: Derived) -> (i32, i32) {
return (d.d, d.base.a);
}

// CHECK:STDOUT: --- fail_abstract.carbon
// --- fail_abstract_decl.carbon
// CHECK:STDERR: fail_abstract_decl.carbon:[[@LINE+3]]:1: error: `abstract` not allowed on `class` declaration, only definition
// CHECK:STDERR: abstract class AbstractDecl;
// CHECK:STDERR: ^~~~~~~~
abstract class AbstractDecl;

// CHECK:STDOUT: --- fail_todo_rejects_valid_abstract_subobject_construction.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Abstract: type = class_type @Abstract [template]
Expand All @@ -54,11 +73,6 @@ fn Access(d: Derived) -> (i32, i32) {
// CHECK:STDOUT: %.13: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.14: i32 = int_literal 7 [template]
// CHECK:STDOUT: %.15: type = struct_type {.base: %.3, .d: i32} [template]
// CHECK:STDOUT: %.16: type = tuple_type (type, type) [template]
// CHECK:STDOUT: %.17: type = tuple_type (i32, i32) [template]
// CHECK:STDOUT: %Access.type: type = fn_type @Access [template]
// CHECK:STDOUT: %Access: %Access.type = struct_value () [template]
// CHECK:STDOUT: %.18: type = ptr_type %.17 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
Expand All @@ -82,7 +96,6 @@ fn Access(d: Derived) -> (i32, i32) {
// CHECK:STDOUT: .Abstract = %Abstract.decl
// CHECK:STDOUT: .Derived = %Derived.decl
// CHECK:STDOUT: .Make = %Make.decl
// CHECK:STDOUT: .Access = %Access.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %Abstract.decl: type = class_decl @Abstract [template = constants.%Abstract] {} {}
Expand All @@ -91,83 +104,136 @@ fn Access(d: Derived) -> (i32, i32) {
// CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [template = constants.%Derived]
// CHECK:STDOUT: %return: ref %Derived = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [template = constants.%Access] {
// CHECK:STDOUT: %d.patt: %Derived = binding_pattern d
// CHECK:STDOUT: } {
// CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [template = constants.%Derived]
// CHECK:STDOUT: %d.param: %Derived = param d, runtime_param0
// CHECK:STDOUT: %d: %Derived = bind_name d, %d.param
// CHECK:STDOUT: %int.make_type_32.loc29_27: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %int.make_type_32.loc29_32: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %.loc29_35.1: %.16 = tuple_literal (%int.make_type_32.loc29_27, %int.make_type_32.loc29_32)
// CHECK:STDOUT: %.loc29_35.2: type = value_of_initializer %int.make_type_32.loc29_27 [template = i32]
// CHECK:STDOUT: %.loc29_35.3: type = converted %int.make_type_32.loc29_27, %.loc29_35.2 [template = i32]
// CHECK:STDOUT: %.loc29_35.4: type = value_of_initializer %int.make_type_32.loc29_32 [template = i32]
// CHECK:STDOUT: %.loc29_35.5: type = converted %int.make_type_32.loc29_32, %.loc29_35.4 [template = i32]
// CHECK:STDOUT: %.loc29_35.6: type = converted %.loc29_35.1, constants.%.17 [template = constants.%.17]
// CHECK:STDOUT: %return: ref %.17 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Abstract {
// CHECK:STDOUT: %int.make_type_32: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %.loc12_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
// CHECK:STDOUT: %.loc12_10.2: type = converted %int.make_type_32, %.loc12_10.1 [template = i32]
// CHECK:STDOUT: %.loc12_8: %.2 = field_decl a, element0 [template]
// CHECK:STDOUT: %.loc13: <witness> = complete_type_witness %.3 [template = constants.%.4]
// CHECK:STDOUT: %.loc2_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
// CHECK:STDOUT: %.loc2_10.2: type = converted %int.make_type_32, %.loc2_10.1 [template = i32]
// CHECK:STDOUT: %.loc2_8: %.2 = field_decl a, element0 [template]
// CHECK:STDOUT: %.loc3: <witness> = complete_type_witness %.3 [template = constants.%.4]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Abstract
// CHECK:STDOUT: .a = %.loc12_8
// CHECK:STDOUT: .a = %.loc2_8
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Derived {
// CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract.decl [template = constants.%Abstract]
// CHECK:STDOUT: %.loc16: %.6 = base_decl %Abstract, element0 [template]
// CHECK:STDOUT: %.loc6: %.6 = base_decl %Abstract, element0 [template]
// CHECK:STDOUT: %int.make_type_32: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %.loc18_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
// CHECK:STDOUT: %.loc18_10.2: type = converted %int.make_type_32, %.loc18_10.1 [template = i32]
// CHECK:STDOUT: %.loc18_8: %.7 = field_decl d, element1 [template]
// CHECK:STDOUT: %.loc19: <witness> = complete_type_witness %.8 [template = constants.%.9]
// CHECK:STDOUT: %.loc8_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
// CHECK:STDOUT: %.loc8_10.2: type = converted %int.make_type_32, %.loc8_10.1 [template = i32]
// CHECK:STDOUT: %.loc8_8: %.7 = field_decl d, element1 [template]
// CHECK:STDOUT: %.loc9: <witness> = complete_type_witness %.8 [template = constants.%.9]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Derived
// CHECK:STDOUT: .base = %.loc16
// CHECK:STDOUT: .d = %.loc18_8
// CHECK:STDOUT: .base = %.loc6
// CHECK:STDOUT: .d = %.loc8_8
// CHECK:STDOUT: extend name_scope2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Make() -> %return: %Derived {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc26_25: i32 = int_literal 1 [template = constants.%.13]
// CHECK:STDOUT: %.loc26_26: %.3 = struct_literal (%.loc26_25)
// CHECK:STDOUT: %.loc26_34: i32 = int_literal 7 [template = constants.%.14]
// CHECK:STDOUT: %.loc26_35: %.15 = struct_literal (%.loc26_26, %.loc26_34)
// CHECK:STDOUT: %.loc19_25: i32 = int_literal 1 [template = constants.%.13]
// CHECK:STDOUT: %.loc19_26: %.3 = struct_literal (%.loc19_25)
// CHECK:STDOUT: %.loc19_34: i32 = int_literal 7 [template = constants.%.14]
// CHECK:STDOUT: %.loc19_35: %.15 = struct_literal (%.loc19_26, %.loc19_34)
// CHECK:STDOUT: return <error> to %return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Access(%d: %Derived) -> %return: %.17 {
// CHECK:STDOUT: --- fail_todo_rejects_valid_abstract_subobject_access.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template]
// CHECK:STDOUT: %.1: type = tuple_type () [template]
// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template]
// CHECK:STDOUT: %.2: type = tuple_type (type, type) [template]
// CHECK:STDOUT: %.3: type = tuple_type (i32, i32) [template]
// CHECK:STDOUT: %Access.type: type = fn_type @Access [template]
// CHECK:STDOUT: %Access: %Access.type = struct_value () [template]
// CHECK:STDOUT: %.4: type = ptr_type %.3 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: .Int32 = %import_ref
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/operators
// CHECK:STDOUT: import Core//prelude/types
// CHECK:STDOUT: import Core//prelude/operators/arithmetic
// CHECK:STDOUT: import Core//prelude/operators/as
// CHECK:STDOUT: import Core//prelude/operators/bitwise
// CHECK:STDOUT: import Core//prelude/operators/comparison
// CHECK:STDOUT: import Core//prelude/types/bool
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: %Int32.type = import_ref Core//prelude/types, inst+4, loaded [template = constants.%Int32]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .Access = %Access.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %Access.decl: %Access.type = fn_decl @Access [template = constants.%Access] {
// CHECK:STDOUT: %d.patt: <error> = binding_pattern d
// CHECK:STDOUT: } {
// CHECK:STDOUT: %Derived.ref: <error> = name_ref Derived, <error> [template = <error>]
// CHECK:STDOUT: %d.param: <error> = param d, runtime_param0
// CHECK:STDOUT: %d: <error> = bind_name d, %d.param
// CHECK:STDOUT: %int.make_type_32.loc8_27: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %int.make_type_32.loc8_32: init type = call constants.%Int32() [template = i32]
// CHECK:STDOUT: %.loc8_35.1: %.2 = tuple_literal (%int.make_type_32.loc8_27, %int.make_type_32.loc8_32)
// CHECK:STDOUT: %.loc8_35.2: type = value_of_initializer %int.make_type_32.loc8_27 [template = i32]
// CHECK:STDOUT: %.loc8_35.3: type = converted %int.make_type_32.loc8_27, %.loc8_35.2 [template = i32]
// CHECK:STDOUT: %.loc8_35.4: type = value_of_initializer %int.make_type_32.loc8_32 [template = i32]
// CHECK:STDOUT: %.loc8_35.5: type = converted %int.make_type_32.loc8_32, %.loc8_35.4 [template = i32]
// CHECK:STDOUT: %.loc8_35.6: type = converted %.loc8_35.1, constants.%.3 [template = constants.%.3]
// CHECK:STDOUT: %return: ref %.3 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Access(%d: <error>) -> %return: %.3 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %d.ref.loc30_11: %Derived = name_ref d, %d
// CHECK:STDOUT: %d.ref.loc30_12: %.7 = name_ref d, @Derived.%.loc18_8 [template = @Derived.%.loc18_8]
// CHECK:STDOUT: %.loc30_12.1: ref i32 = class_element_access %d.ref.loc30_11, element1
// CHECK:STDOUT: %.loc30_12.2: i32 = bind_value %.loc30_12.1
// CHECK:STDOUT: %d.ref.loc30_16: %Derived = name_ref d, %d
// CHECK:STDOUT: %base.ref: %.6 = name_ref base, @Derived.%.loc16 [template = @Derived.%.loc16]
// CHECK:STDOUT: %.loc30_17.1: ref %Abstract = class_element_access %d.ref.loc30_16, element0
// CHECK:STDOUT: %.loc30_17.2: %Abstract = bind_value %.loc30_17.1
// CHECK:STDOUT: %a.ref: %.2 = name_ref a, @Abstract.%.loc12_8 [template = @Abstract.%.loc12_8]
// CHECK:STDOUT: %.loc30_22.1: ref i32 = class_element_access %.loc30_17.2, element0
// CHECK:STDOUT: %.loc30_22.2: i32 = bind_value %.loc30_22.1
// CHECK:STDOUT: %.loc30_24.1: %.17 = tuple_literal (%.loc30_12.2, %.loc30_22.2)
// CHECK:STDOUT: %.loc30_24.2: ref i32 = tuple_access %return, element0
// CHECK:STDOUT: %.loc30_24.3: init i32 = initialize_from %.loc30_12.2 to %.loc30_24.2
// CHECK:STDOUT: %.loc30_24.4: ref i32 = tuple_access %return, element1
// CHECK:STDOUT: %.loc30_24.5: init i32 = initialize_from %.loc30_22.2 to %.loc30_24.4
// CHECK:STDOUT: %.loc30_24.6: init %.17 = tuple_init (%.loc30_24.3, %.loc30_24.5) to %return
// CHECK:STDOUT: %.loc30_25: init %.17 = converted %.loc30_24.1, %.loc30_24.6
// CHECK:STDOUT: return %.loc30_25 to %return
// CHECK:STDOUT: %d.ref.loc9_11: <error> = name_ref d, %d
// CHECK:STDOUT: %d.ref.loc9_16: <error> = name_ref d, %d
// CHECK:STDOUT: %.loc9: <error> = tuple_literal (<error>, <error>)
// CHECK:STDOUT: return <error> to %return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_abstract_decl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %AbstractDecl: type = class_type @AbstractDecl [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/operators
// CHECK:STDOUT: import Core//prelude/types
// CHECK:STDOUT: import Core//prelude/operators/arithmetic
// CHECK:STDOUT: import Core//prelude/operators/as
// CHECK:STDOUT: import Core//prelude/operators/bitwise
// CHECK:STDOUT: import Core//prelude/operators/comparison
// CHECK:STDOUT: import Core//prelude/types/bool
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .AbstractDecl = %AbstractDecl.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %AbstractDecl.decl: type = class_decl @AbstractDecl [template = constants.%AbstractDecl] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @AbstractDecl;
// CHECK:STDOUT:
Loading

0 comments on commit b1014bf

Please sign in to comment.