diff --git a/toolchain/check/decl_state.h b/toolchain/check/decl_state.h index 9d14df8b7572..695d9795ff48 100644 --- a/toolchain/check/decl_state.h +++ b/toolchain/check/decl_state.h @@ -23,9 +23,10 @@ enum class KeywordModifierSet { Abstract = 1 << 2, Base = 1 << 3, Default = 1 << 4, - Final = 1 << 5, - Impl = 1 << 6, - Virtual = 1 << 7, + Extend = 1 << 5, + Final = 1 << 6, + Impl = 1 << 7, + Virtual = 1 << 8, // Sets of modifiers: Access = Private | Protected, @@ -45,7 +46,7 @@ inline auto operator!(KeywordModifierSet k) -> bool { // declaration and the keyword modifiers that apply to that declaration. struct DeclState { // What kind of declaration - enum DeclKind { FileScope, Class, Constraint, Fn, Interface, Let, Var }; + enum DeclKind { FileScope, Class, Base, Constraint, Fn, Interface, Let, Var }; explicit DeclState(DeclKind decl_kind, Parse::NodeId parse_node) : kind(decl_kind), first_node(parse_node) {} @@ -86,7 +87,8 @@ class DeclStateStack { // Exits a declaration of kind `k`. auto Pop(DeclState::DeclKind k) -> void { - CARBON_CHECK(stack_.back().kind == k); + CARBON_CHECK(stack_.back().kind == k) + << "Found: " << stack_.back().kind << " expected: " << k; stack_.pop_back(); CARBON_CHECK(!stack_.empty()); } diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index e4f9997040e7..e2d13589b37d 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -165,8 +165,8 @@ auto HandleClassDefinitionStart(Context& context, Parse::NodeId parse_node) return true; } -auto HandleBaseIntroducer(Context& /*context*/, Parse::NodeId /*parse_node*/) - -> bool { +auto HandleBaseIntroducer(Context& context, Parse::NodeId parse_node) -> bool { + context.decl_state_stack().Push(DeclState::Base, parse_node); return true; } @@ -178,6 +178,18 @@ auto HandleBaseColon(Context& /*context*/, Parse::NodeId /*parse_node*/) auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool { auto base_type_expr_id = context.node_stack().PopExpr(); + // Process modifiers. `extend` is required, none others are allowed. + LimitModifiersOnDecl(context, KeywordModifierSet::Extend, + Lex::TokenKind::Base); + auto modifiers = context.decl_state_stack().innermost().modifier_set; + if (!(modifiers & KeywordModifierSet::Extend)) { + CARBON_DIAGNOSTIC(BaseMissingExtend, Error, + "Missing `extend` before `base` declaration in class."); + context.emitter().Emit(context.decl_state_stack().innermost().first_node, + BaseMissingExtend); + } + context.decl_state_stack().Pop(DeclState::Base); + auto enclosing_class_decl = context.GetCurrentScopeAs(); if (!enclosing_class_decl) { CARBON_DIAGNOSTIC(BaseOutsideClass, Error, diff --git a/toolchain/check/handle_modifier.cpp b/toolchain/check/handle_modifier.cpp index cac2e341f204..8413a3fafa48 100644 --- a/toolchain/check/handle_modifier.cpp +++ b/toolchain/check/handle_modifier.cpp @@ -83,6 +83,8 @@ static auto GetDeclModifierEnum(Lex::TokenKind token_kind) return KeywordModifierSet::Base; case Lex::TokenKind::Default: return KeywordModifierSet::Default; + case Lex::TokenKind::Extend: + return KeywordModifierSet::Extend; case Lex::TokenKind::Final: return KeywordModifierSet::Final; case Lex::TokenKind::Impl: diff --git a/toolchain/check/testdata/class/base.carbon b/toolchain/check/testdata/class/base.carbon index 0abb11ba3a1d..a27edaca7da3 100644 --- a/toolchain/check/testdata/class/base.carbon +++ b/toolchain/check/testdata/class/base.carbon @@ -9,8 +9,7 @@ base class Base { } class Derived { - // TODO: `extend base: Base;` - base: Base; + extend base: Base; var d: i32; } @@ -28,14 +27,14 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.loc9: type = struct_type {.b: i32} // CHECK:STDOUT: %.loc7: type = ptr_type {.b: i32} -// CHECK:STDOUT: %.loc16_1.1: type = struct_type {.base: Base, .d: i32} -// CHECK:STDOUT: %.loc16_1.2: type = struct_type {.base: {.b: i32}*, .d: i32} -// CHECK:STDOUT: %.loc16_1.3: type = ptr_type {.base: {.b: i32}*, .d: i32} +// CHECK:STDOUT: %.loc15_1.1: type = struct_type {.base: Base, .d: i32} +// CHECK:STDOUT: %.loc15_1.2: type = struct_type {.base: {.b: i32}*, .d: i32} +// CHECK:STDOUT: %.loc15_1.3: type = ptr_type {.base: {.b: i32}*, .d: i32} // CHECK:STDOUT: %.loc11: type = ptr_type {.base: Base, .d: i32} -// CHECK:STDOUT: %.loc19: type = struct_type {.base: {.b: i32}, .d: i32} -// CHECK:STDOUT: %.loc22_35.1: type = tuple_type (type, type) -// CHECK:STDOUT: %.loc22_35.2: type = tuple_type (i32, i32) -// CHECK:STDOUT: %.loc22_35.3: type = ptr_type (i32, i32) +// CHECK:STDOUT: %.loc18: type = struct_type {.base: {.b: i32}, .d: i32} +// CHECK:STDOUT: %.loc21_35.1: type = tuple_type (type, type) +// CHECK:STDOUT: %.loc21_35.2: type = tuple_type (i32, i32) +// CHECK:STDOUT: %.loc21_35.3: type = ptr_type (i32, i32) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -59,52 +58,52 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base -// CHECK:STDOUT: %.loc13_13.1: type = unbound_element_type Derived, Base -// CHECK:STDOUT: %.loc13_13.2: = base_decl Base, element0 -// CHECK:STDOUT: %.loc15_8.1: type = unbound_element_type Derived, i32 -// CHECK:STDOUT: %.loc15_8.2: = field_decl d, element1 -// CHECK:STDOUT: %d: = bind_name d, %.loc15_8.2 +// CHECK:STDOUT: %.loc12_20.1: type = unbound_element_type Derived, Base +// CHECK:STDOUT: %.loc12_20.2: = base_decl Base, element0 +// CHECK:STDOUT: %.loc14_8.1: type = unbound_element_type Derived, i32 +// CHECK:STDOUT: %.loc14_8.2: = field_decl d, element1 +// CHECK:STDOUT: %d: = bind_name d, %.loc14_8.2 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc13_13.2 +// CHECK:STDOUT: .base = %.loc12_20.2 // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> %return: Derived { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc19_25: i32 = int_literal 4 -// CHECK:STDOUT: %.loc19_26.1: {.b: i32} = struct_literal (%.loc19_25) -// CHECK:STDOUT: %.loc19_34: i32 = int_literal 7 -// CHECK:STDOUT: %.loc19_35.1: {.base: {.b: i32}, .d: i32} = struct_literal (%.loc19_26.1, %.loc19_34) -// CHECK:STDOUT: %.loc19_35.2: ref Base = class_element_access %return, element0 -// CHECK:STDOUT: %.loc19_26.2: ref i32 = class_element_access %.loc19_35.2, element0 -// CHECK:STDOUT: %.loc19_26.3: init i32 = initialize_from %.loc19_25 to %.loc19_26.2 -// CHECK:STDOUT: %.loc19_26.4: init Base = class_init (%.loc19_26.3), %.loc19_35.2 -// CHECK:STDOUT: %.loc19_26.5: init Base = converted %.loc19_26.1, %.loc19_26.4 -// CHECK:STDOUT: %.loc19_35.3: ref i32 = class_element_access %return, element1 -// CHECK:STDOUT: %.loc19_35.4: init i32 = initialize_from %.loc19_34 to %.loc19_35.3 -// CHECK:STDOUT: %.loc19_35.5: init Derived = class_init (%.loc19_26.5, %.loc19_35.4), %return -// CHECK:STDOUT: %.loc19_35.6: init Derived = converted %.loc19_35.1, %.loc19_35.5 -// CHECK:STDOUT: return %.loc19_35.6 +// CHECK:STDOUT: %.loc18_25: i32 = int_literal 4 +// CHECK:STDOUT: %.loc18_26.1: {.b: i32} = struct_literal (%.loc18_25) +// CHECK:STDOUT: %.loc18_34: i32 = int_literal 7 +// CHECK:STDOUT: %.loc18_35.1: {.base: {.b: i32}, .d: i32} = struct_literal (%.loc18_26.1, %.loc18_34) +// CHECK:STDOUT: %.loc18_35.2: ref Base = class_element_access %return, element0 +// CHECK:STDOUT: %.loc18_26.2: ref i32 = class_element_access %.loc18_35.2, element0 +// CHECK:STDOUT: %.loc18_26.3: init i32 = initialize_from %.loc18_25 to %.loc18_26.2 +// CHECK:STDOUT: %.loc18_26.4: init Base = class_init (%.loc18_26.3), %.loc18_35.2 +// CHECK:STDOUT: %.loc18_26.5: init Base = converted %.loc18_26.1, %.loc18_26.4 +// CHECK:STDOUT: %.loc18_35.3: ref i32 = class_element_access %return, element1 +// CHECK:STDOUT: %.loc18_35.4: init i32 = initialize_from %.loc18_34 to %.loc18_35.3 +// CHECK:STDOUT: %.loc18_35.5: init Derived = class_init (%.loc18_26.5, %.loc18_35.4), %return +// CHECK:STDOUT: %.loc18_35.6: init Derived = converted %.loc18_35.1, %.loc18_35.5 +// CHECK:STDOUT: return %.loc18_35.6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d: Derived) -> %return: (i32, i32) { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %d.ref.loc23_11: Derived = name_ref d, %d -// CHECK:STDOUT: %.loc23_12.1: ref i32 = class_element_access %d.ref.loc23_11, element1 -// CHECK:STDOUT: %.loc23_12.2: i32 = bind_value %.loc23_12.1 -// CHECK:STDOUT: %d.ref.loc23_16: Derived = name_ref d, %d -// CHECK:STDOUT: %.loc23_17.1: ref Base = class_element_access %d.ref.loc23_16, element0 -// CHECK:STDOUT: %.loc23_17.2: Base = bind_value %.loc23_17.1 -// CHECK:STDOUT: %.loc23_22.1: ref i32 = class_element_access %.loc23_17.2, element0 -// CHECK:STDOUT: %.loc23_22.2: i32 = bind_value %.loc23_22.1 -// CHECK:STDOUT: %.loc23_24.1: (i32, i32) = tuple_literal (%.loc23_12.2, %.loc23_22.2) -// CHECK:STDOUT: %.loc23_24.2: ref i32 = tuple_access %return, element0 -// CHECK:STDOUT: %.loc23_24.3: init i32 = initialize_from %.loc23_12.2 to %.loc23_24.2 -// CHECK:STDOUT: %.loc23_24.4: ref i32 = tuple_access %return, element1 -// CHECK:STDOUT: %.loc23_24.5: init i32 = initialize_from %.loc23_22.2 to %.loc23_24.4 -// CHECK:STDOUT: %.loc23_24.6: init (i32, i32) = tuple_init (%.loc23_24.3, %.loc23_24.5) to %return -// CHECK:STDOUT: %.loc23_24.7: init (i32, i32) = converted %.loc23_24.1, %.loc23_24.6 -// CHECK:STDOUT: return %.loc23_24.7 +// CHECK:STDOUT: %d.ref.loc22_11: Derived = name_ref d, %d +// CHECK:STDOUT: %.loc22_12.1: ref i32 = class_element_access %d.ref.loc22_11, element1 +// CHECK:STDOUT: %.loc22_12.2: i32 = bind_value %.loc22_12.1 +// CHECK:STDOUT: %d.ref.loc22_16: Derived = name_ref d, %d +// CHECK:STDOUT: %.loc22_17.1: ref Base = class_element_access %d.ref.loc22_16, element0 +// CHECK:STDOUT: %.loc22_17.2: Base = bind_value %.loc22_17.1 +// CHECK:STDOUT: %.loc22_22.1: ref i32 = class_element_access %.loc22_17.2, element0 +// CHECK:STDOUT: %.loc22_22.2: i32 = bind_value %.loc22_22.1 +// CHECK:STDOUT: %.loc22_24.1: (i32, i32) = tuple_literal (%.loc22_12.2, %.loc22_22.2) +// CHECK:STDOUT: %.loc22_24.2: ref i32 = tuple_access %return, element0 +// CHECK:STDOUT: %.loc22_24.3: init i32 = initialize_from %.loc22_12.2 to %.loc22_24.2 +// CHECK:STDOUT: %.loc22_24.4: ref i32 = tuple_access %return, element1 +// CHECK:STDOUT: %.loc22_24.5: init i32 = initialize_from %.loc22_22.2 to %.loc22_24.4 +// CHECK:STDOUT: %.loc22_24.6: init (i32, i32) = tuple_init (%.loc22_24.3, %.loc22_24.5) to %return +// CHECK:STDOUT: %.loc22_24.7: init (i32, i32) = converted %.loc22_24.1, %.loc22_24.6 +// CHECK:STDOUT: return %.loc22_24.7 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_abstract.carbon b/toolchain/check/testdata/class/fail_abstract.carbon index 4ce54535da75..635bd6c674dd 100644 --- a/toolchain/check/testdata/class/fail_abstract.carbon +++ b/toolchain/check/testdata/class/fail_abstract.carbon @@ -9,8 +9,7 @@ abstract class Abstract { } class Derived { - // TODO: `extend base: Base;` - base: Abstract; + extend base: Abstract; var d: i32; } @@ -32,14 +31,14 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.loc9: type = struct_type {.a: i32} // CHECK:STDOUT: %.loc7: type = ptr_type {.a: i32} -// CHECK:STDOUT: %.loc16_1.1: type = struct_type {.base: Abstract, .d: i32} -// CHECK:STDOUT: %.loc16_1.2: type = struct_type {.base: {.a: i32}*, .d: i32} -// CHECK:STDOUT: %.loc16_1.3: type = ptr_type {.base: {.a: i32}*, .d: i32} +// CHECK:STDOUT: %.loc15_1.1: type = struct_type {.base: Abstract, .d: i32} +// CHECK:STDOUT: %.loc15_1.2: type = struct_type {.base: {.a: i32}*, .d: i32} +// CHECK:STDOUT: %.loc15_1.3: type = ptr_type {.base: {.a: i32}*, .d: i32} // CHECK:STDOUT: %.loc11: type = ptr_type {.base: Abstract, .d: i32} -// CHECK:STDOUT: %.loc23: type = struct_type {.base: {.a: i32}, .d: i32} -// CHECK:STDOUT: %.loc26_35.1: type = tuple_type (type, type) -// CHECK:STDOUT: %.loc26_35.2: type = tuple_type (i32, i32) -// CHECK:STDOUT: %.loc26_35.3: type = ptr_type (i32, i32) +// CHECK:STDOUT: %.loc22: type = struct_type {.base: {.a: i32}, .d: i32} +// CHECK:STDOUT: %.loc25_35.1: type = tuple_type (type, type) +// CHECK:STDOUT: %.loc25_35.2: type = tuple_type (i32, i32) +// CHECK:STDOUT: %.loc25_35.3: type = ptr_type (i32, i32) // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -63,43 +62,43 @@ fn Access(d: Derived) -> (i32, i32) { // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Abstract.ref: type = name_ref Abstract, file.%Abstract -// CHECK:STDOUT: %.loc13_17.1: type = unbound_element_type Derived, Abstract -// CHECK:STDOUT: %.loc13_17.2: = base_decl Abstract, element0 -// CHECK:STDOUT: %.loc15_8.1: type = unbound_element_type Derived, i32 -// CHECK:STDOUT: %.loc15_8.2: = field_decl d, element1 -// CHECK:STDOUT: %d: = bind_name d, %.loc15_8.2 +// CHECK:STDOUT: %.loc12_24.1: type = unbound_element_type Derived, Abstract +// CHECK:STDOUT: %.loc12_24.2: = base_decl Abstract, element0 +// CHECK:STDOUT: %.loc14_8.1: type = unbound_element_type Derived, i32 +// CHECK:STDOUT: %.loc14_8.2: = field_decl d, element1 +// CHECK:STDOUT: %d: = bind_name d, %.loc14_8.2 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc13_17.2 +// CHECK:STDOUT: .base = %.loc12_24.2 // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> %return: Derived { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc23_25: i32 = int_literal 1 -// CHECK:STDOUT: %.loc23_26: {.a: i32} = struct_literal (%.loc23_25) -// CHECK:STDOUT: %.loc23_34: i32 = int_literal 7 -// CHECK:STDOUT: %.loc23_35: {.base: {.a: i32}, .d: i32} = struct_literal (%.loc23_26, %.loc23_34) +// CHECK:STDOUT: %.loc22_25: i32 = int_literal 1 +// CHECK:STDOUT: %.loc22_26: {.a: i32} = struct_literal (%.loc22_25) +// CHECK:STDOUT: %.loc22_34: i32 = int_literal 7 +// CHECK:STDOUT: %.loc22_35: {.base: {.a: i32}, .d: i32} = struct_literal (%.loc22_26, %.loc22_34) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Access(%d: Derived) -> %return: (i32, i32) { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %d.ref.loc27_11: Derived = name_ref d, %d -// CHECK:STDOUT: %.loc27_12.1: ref i32 = class_element_access %d.ref.loc27_11, element1 -// CHECK:STDOUT: %.loc27_12.2: i32 = bind_value %.loc27_12.1 -// CHECK:STDOUT: %d.ref.loc27_16: Derived = name_ref d, %d -// CHECK:STDOUT: %.loc27_17.1: ref Abstract = class_element_access %d.ref.loc27_16, element0 -// CHECK:STDOUT: %.loc27_17.2: Abstract = bind_value %.loc27_17.1 -// CHECK:STDOUT: %.loc27_22.1: ref i32 = class_element_access %.loc27_17.2, element0 -// CHECK:STDOUT: %.loc27_22.2: i32 = bind_value %.loc27_22.1 -// CHECK:STDOUT: %.loc27_24.1: (i32, i32) = tuple_literal (%.loc27_12.2, %.loc27_22.2) -// CHECK:STDOUT: %.loc27_24.2: ref i32 = tuple_access %return, element0 -// CHECK:STDOUT: %.loc27_24.3: init i32 = initialize_from %.loc27_12.2 to %.loc27_24.2 -// CHECK:STDOUT: %.loc27_24.4: ref i32 = tuple_access %return, element1 -// CHECK:STDOUT: %.loc27_24.5: init i32 = initialize_from %.loc27_22.2 to %.loc27_24.4 -// CHECK:STDOUT: %.loc27_24.6: init (i32, i32) = tuple_init (%.loc27_24.3, %.loc27_24.5) to %return -// CHECK:STDOUT: %.loc27_24.7: init (i32, i32) = converted %.loc27_24.1, %.loc27_24.6 -// CHECK:STDOUT: return %.loc27_24.7 +// CHECK:STDOUT: %d.ref.loc26_11: Derived = name_ref d, %d +// CHECK:STDOUT: %.loc26_12.1: ref i32 = class_element_access %d.ref.loc26_11, element1 +// CHECK:STDOUT: %.loc26_12.2: i32 = bind_value %.loc26_12.1 +// CHECK:STDOUT: %d.ref.loc26_16: Derived = name_ref d, %d +// CHECK:STDOUT: %.loc26_17.1: ref Abstract = class_element_access %d.ref.loc26_16, element0 +// CHECK:STDOUT: %.loc26_17.2: Abstract = bind_value %.loc26_17.1 +// CHECK:STDOUT: %.loc26_22.1: ref i32 = class_element_access %.loc26_17.2, element0 +// CHECK:STDOUT: %.loc26_22.2: i32 = bind_value %.loc26_22.1 +// CHECK:STDOUT: %.loc26_24.1: (i32, i32) = tuple_literal (%.loc26_12.2, %.loc26_22.2) +// CHECK:STDOUT: %.loc26_24.2: ref i32 = tuple_access %return, element0 +// CHECK:STDOUT: %.loc26_24.3: init i32 = initialize_from %.loc26_12.2 to %.loc26_24.2 +// CHECK:STDOUT: %.loc26_24.4: ref i32 = tuple_access %return, element1 +// CHECK:STDOUT: %.loc26_24.5: init i32 = initialize_from %.loc26_22.2 to %.loc26_24.4 +// CHECK:STDOUT: %.loc26_24.6: init (i32, i32) = tuple_init (%.loc26_24.3, %.loc26_24.5) to %return +// CHECK:STDOUT: %.loc26_24.7: init (i32, i32) = converted %.loc26_24.1, %.loc26_24.6 +// CHECK:STDOUT: return %.loc26_24.7 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_bad_type.carbon b/toolchain/check/testdata/class/fail_base_bad_type.carbon index 9205f1cd1c9f..0e5aabacf412 100644 --- a/toolchain/check/testdata/class/fail_base_bad_type.carbon +++ b/toolchain/check/testdata/class/fail_base_bad_type.carbon @@ -8,52 +8,52 @@ base class Base {} class Final {} class DeriveFromNonType { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:11: ERROR: Cannot implicitly convert from `i32` to `type`. - // CHECK:STDERR: base: 32; - // CHECK:STDERR: ^ - base: 32; + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:18: ERROR: Cannot implicitly convert from `i32` to `type`. + // CHECK:STDERR: extend base: 32; + // CHECK:STDERR: ^ + extend base: 32; } class DeriveFromi32 { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:12: ERROR: Deriving from final type `i32`. Base type must be an `abstract` or `base` class. - // CHECK:STDERR: base: i32; - // CHECK:STDERR: ^ - base: i32; + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:19: ERROR: Deriving from final type `i32`. Base type must be an `abstract` or `base` class. + // CHECK:STDERR: extend base: i32; + // CHECK:STDERR: ^ + extend base: i32; } class DeriveFromTuple { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:16: ERROR: Deriving from final type `(Base,)`. Base type must be an `abstract` or `base` class. - // CHECK:STDERR: base: (Base,); - // CHECK:STDERR: ^ - base: (Base,); + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:23: ERROR: Deriving from final type `(Base,)`. Base type must be an `abstract` or `base` class. + // CHECK:STDERR: extend base: (Base,); + // CHECK:STDERR: ^ + extend base: (Base,); } // TODO: Should we allow this? // We do allow `{.base = {.a: i32, .b: i32}}`. class DeriveFromStruct { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:27: ERROR: Deriving from final type `{.a: i32, .b: i32}`. Base type must be an `abstract` or `base` class. - // CHECK:STDERR: base: {.a: i32, .b: i32}; - // CHECK:STDERR: ^ - base: {.a: i32, .b: i32}; + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:34: ERROR: Deriving from final type `{.a: i32, .b: i32}`. Base type must be an `abstract` or `base` class. + // CHECK:STDERR: extend base: {.a: i32, .b: i32}; + // CHECK:STDERR: ^ + extend base: {.a: i32, .b: i32}; } base class Incomplete; class DeriveFromIncomplete { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+6]]:19: ERROR: Base `Incomplete` is an incomplete type. - // CHECK:STDERR: base: Incomplete; - // CHECK:STDERR: ^ + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+6]]:26: ERROR: Base `Incomplete` is an incomplete type. + // CHECK:STDERR: extend base: Incomplete; + // CHECK:STDERR: ^ // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE-6]]:1: Class was forward declared here. // CHECK:STDERR: base class Incomplete; // CHECK:STDERR: ^~~~ - base: Incomplete; + extend base: Incomplete; } class DeriveFromFinal { - // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:14: ERROR: Deriving from final type `Final`. Base type must be an `abstract` or `base` class. - // CHECK:STDERR: base: Final; - // CHECK:STDERR: ^ - base: Final; + // CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:21: ERROR: Deriving from final type `Final`. Base type must be an `abstract` or `base` class. + // CHECK:STDERR: extend base: Final; + // CHECK:STDERR: ^ + extend base: Final; } // CHECK:STDOUT: --- fail_base_bad_type.carbon @@ -62,11 +62,11 @@ class DeriveFromFinal { // CHECK:STDOUT: %.loc7_18.1: type = struct_type {} // CHECK:STDOUT: %.loc15: type = struct_type {.base: } // CHECK:STDOUT: %.loc22: type = struct_type {.base: i32} -// CHECK:STDOUT: %.loc28_15: type = tuple_type (type) -// CHECK:STDOUT: %.loc28_16.1: type = tuple_type (Base) +// CHECK:STDOUT: %.loc28_22: type = tuple_type (type) +// CHECK:STDOUT: %.loc28_23.1: type = tuple_type (Base) // CHECK:STDOUT: %.loc7_18.2: type = tuple_type () // CHECK:STDOUT: %.loc7_1: type = ptr_type {} -// CHECK:STDOUT: %.loc28_16.2: type = tuple_type ({}*) +// CHECK:STDOUT: %.loc28_23.2: type = tuple_type ({}*) // CHECK:STDOUT: %.loc29: type = struct_type {.base: (Base,)} // CHECK:STDOUT: %.loc37: type = ptr_type {.a: i32, .b: i32} // CHECK:STDOUT: %.loc38: type = struct_type {.base: {.a: i32, .b: i32}} @@ -106,59 +106,59 @@ class DeriveFromFinal { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromNonType { -// CHECK:STDOUT: %.loc14_9: i32 = int_literal 32 -// CHECK:STDOUT: %.loc14_11.1: type = unbound_element_type DeriveFromNonType, -// CHECK:STDOUT: %.loc14_11.2: = base_decl , element0 +// CHECK:STDOUT: %.loc14_16: i32 = int_literal 32 +// CHECK:STDOUT: %.loc14_18.1: type = unbound_element_type DeriveFromNonType, +// CHECK:STDOUT: %.loc14_18.2: = base_decl , element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc14_11.2 +// CHECK:STDOUT: .base = %.loc14_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromi32 { -// CHECK:STDOUT: %.loc21_12.1: type = unbound_element_type DeriveFromi32, i32 -// CHECK:STDOUT: %.loc21_12.2: = base_decl i32, element0 +// CHECK:STDOUT: %.loc21_19.1: type = unbound_element_type DeriveFromi32, i32 +// CHECK:STDOUT: %.loc21_19.2: = base_decl i32, element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc21_12.2 +// CHECK:STDOUT: .base = %.loc21_19.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromTuple { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base -// CHECK:STDOUT: %.loc28_15: (type,) = tuple_literal (%Base.ref) -// CHECK:STDOUT: %.loc28_16.1: type = converted %.loc28_15, constants.%.loc28_16.1 -// CHECK:STDOUT: %.loc28_16.2: type = unbound_element_type DeriveFromTuple, (Base,) -// CHECK:STDOUT: %.loc28_16.3: = base_decl (Base,), element0 +// CHECK:STDOUT: %.loc28_22: (type,) = tuple_literal (%Base.ref) +// CHECK:STDOUT: %.loc28_23.1: type = converted %.loc28_22, constants.%.loc28_23.1 +// CHECK:STDOUT: %.loc28_23.2: type = unbound_element_type DeriveFromTuple, (Base,) +// CHECK:STDOUT: %.loc28_23.3: = base_decl (Base,), element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc28_16.3 +// CHECK:STDOUT: .base = %.loc28_23.3 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromStruct { -// CHECK:STDOUT: %.loc37_26: type = struct_type {.a: i32, .b: i32} -// CHECK:STDOUT: %.loc37_27.1: type = unbound_element_type DeriveFromStruct, {.a: i32, .b: i32} -// CHECK:STDOUT: %.loc37_27.2: = base_decl {.a: i32, .b: i32}, element0 +// CHECK:STDOUT: %.loc37_33: type = struct_type {.a: i32, .b: i32} +// CHECK:STDOUT: %.loc37_34.1: type = unbound_element_type DeriveFromStruct, {.a: i32, .b: i32} +// CHECK:STDOUT: %.loc37_34.2: = base_decl {.a: i32, .b: i32}, element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc37_27.2 +// CHECK:STDOUT: .base = %.loc37_34.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Incomplete; // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromIncomplete { // CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete -// CHECK:STDOUT: %.loc49_19.1: type = unbound_element_type DeriveFromIncomplete, -// CHECK:STDOUT: %.loc49_19.2: = base_decl , element0 +// CHECK:STDOUT: %.loc49_26.1: type = unbound_element_type DeriveFromIncomplete, +// CHECK:STDOUT: %.loc49_26.2: = base_decl , element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc49_19.2 +// CHECK:STDOUT: .base = %.loc49_26.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DeriveFromFinal { // CHECK:STDOUT: %Final.ref: type = name_ref Final, file.%Final -// CHECK:STDOUT: %.loc56_14.1: type = unbound_element_type DeriveFromFinal, Final -// CHECK:STDOUT: %.loc56_14.2: = base_decl Final, element0 +// CHECK:STDOUT: %.loc56_21.1: type = unbound_element_type DeriveFromFinal, Final +// CHECK:STDOUT: %.loc56_21.2: = base_decl Final, element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc56_14.2 +// CHECK:STDOUT: .base = %.loc56_21.2 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_misplaced.carbon b/toolchain/check/testdata/class/fail_base_misplaced.carbon index 6d41b23fb4fe..090b0e237fec 100644 --- a/toolchain/check/testdata/class/fail_base_misplaced.carbon +++ b/toolchain/check/testdata/class/fail_base_misplaced.carbon @@ -6,19 +6,19 @@ base class B {} -// CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:8: ERROR: `base` declaration can only be used in a class. -// CHECK:STDERR: base: B; -// CHECK:STDERR: ^ -base: B; +// CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:15: ERROR: `base` declaration can only be used in a class. +// CHECK:STDERR: extend base: B; +// CHECK:STDERR: ^ +extend base: B; fn F() { // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+6]]:3: ERROR: Expected expression. - // CHECK:STDERR: base: B; - // CHECK:STDERR: ^~~~ + // CHECK:STDERR: extend base: B; + // CHECK:STDERR: ^~~~~~ // CHECK:STDERR: fail_base_misplaced.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `HandleInvalidParse`. - // CHECK:STDERR: base: B; - // CHECK:STDERR: ^~~~ - base: B; + // CHECK:STDERR: extend base: B; + // CHECK:STDERR: ^~~~~~ + extend base: B; } // CHECK:STDOUT: --- fail_base_misplaced.carbon diff --git a/toolchain/check/testdata/class/fail_base_modifiers.carbon b/toolchain/check/testdata/class/fail_base_modifiers.carbon new file mode 100644 index 000000000000..fbbb59a0ae15 --- /dev/null +++ b/toolchain/check/testdata/class/fail_base_modifiers.carbon @@ -0,0 +1,109 @@ +// 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 + +base class B {} + +class C1 { + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+3]]:3: ERROR: `private` not allowed on `base` declaration. + // CHECK:STDERR: private extend base: B; + // CHECK:STDERR: ^~~~~~~ + private extend base: B; +} + +class C2 { + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+6]]:3: ERROR: `abstract` not allowed on `base` declaration. + // CHECK:STDERR: abstract base: B; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+3]]:3: ERROR: Missing `extend` before `base` declaration in class. + // CHECK:STDERR: abstract base: B; + // CHECK:STDERR: ^~~~~~~~ + abstract base: B; +} + +class C3 { + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+6]]:10: ERROR: `default` not allowed on declaration with `extend`. + // CHECK:STDERR: extend default base: B; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+3]]:3: `extend` previously appeared here. + // CHECK:STDERR: extend default base: B; + // CHECK:STDERR: ^~~~~~ + extend default base: B; +} + +class C4 { + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+6]]:10: ERROR: `extend` repeated on declaration. + // CHECK:STDERR: extend extend base: B; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: fail_base_modifiers.carbon:[[@LINE+3]]:3: `extend` previously appeared here. + // CHECK:STDERR: extend extend base: B; + // CHECK:STDERR: ^~~~~~ + extend extend base: B; +} + +// CHECK:STDOUT: --- fail_base_modifiers.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.loc7_15.1: type = struct_type {} +// CHECK:STDOUT: %.loc7_15.2: type = tuple_type () +// CHECK:STDOUT: %.loc7_1: type = ptr_type {} +// CHECK:STDOUT: %.loc14: type = struct_type {.base: B} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.B = %B.decl, .C1 = %C1.decl, .C2 = %C2.decl, .C3 = %C3.decl, .C4 = %C4.decl} +// CHECK:STDOUT: %B.decl = class_decl @B, () +// CHECK:STDOUT: %B: type = class_type @B +// CHECK:STDOUT: %C1.decl = class_decl @C1, () +// CHECK:STDOUT: %C1: type = class_type @C1 +// CHECK:STDOUT: %C2.decl = class_decl @C2, () +// CHECK:STDOUT: %C2: type = class_type @C2 +// CHECK:STDOUT: %C3.decl = class_decl @C3, () +// CHECK:STDOUT: %C3: type = class_type @C3 +// CHECK:STDOUT: %C4.decl = class_decl @C4, () +// CHECK:STDOUT: %C4: type = class_type @C4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C1 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B +// CHECK:STDOUT: %.loc13_25.1: type = unbound_element_type C1, B +// CHECK:STDOUT: %.loc13_25.2: = base_decl B, element0 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc13_25.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C2 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B +// CHECK:STDOUT: %.loc23_19.1: type = unbound_element_type C2, B +// CHECK:STDOUT: %.loc23_19.2: = base_decl B, element0 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc23_19.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C3 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B +// CHECK:STDOUT: %.loc33_25.1: type = unbound_element_type C3, B +// CHECK:STDOUT: %.loc33_25.2: = base_decl B, element0 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc33_25.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C4 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B +// CHECK:STDOUT: %.loc43_24.1: type = unbound_element_type C4, B +// CHECK:STDOUT: %.loc43_24.2: = base_decl B, element0 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc43_24.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_no_extend.carbon b/toolchain/check/testdata/class/fail_base_no_extend.carbon new file mode 100644 index 000000000000..98b76ac57557 --- /dev/null +++ b/toolchain/check/testdata/class/fail_base_no_extend.carbon @@ -0,0 +1,46 @@ +// 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 + +base class B {} + +class C { + // CHECK:STDERR: fail_base_no_extend.carbon:[[@LINE+3]]:3: ERROR: Missing `extend` before `base` declaration in class. + // CHECK:STDERR: base: B; + // CHECK:STDERR: ^~~~ + base: B; +} + +// CHECK:STDOUT: --- fail_base_no_extend.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.loc7_15.1: type = struct_type {} +// CHECK:STDOUT: %.loc7_15.2: type = tuple_type () +// CHECK:STDOUT: %.loc7_1: type = ptr_type {} +// CHECK:STDOUT: %.loc14: type = struct_type {.base: B} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.B = %B.decl, .C = %C.decl} +// CHECK:STDOUT: %B.decl = class_decl @B, () +// CHECK:STDOUT: %B: type = class_type @B +// CHECK:STDOUT: %C.decl = class_decl @C, () +// CHECK:STDOUT: %C: type = class_type @C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B +// CHECK:STDOUT: %.loc13_10.1: type = unbound_element_type C, B +// CHECK:STDOUT: %.loc13_10.2: = base_decl B, element0 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .base = %.loc13_10.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_repeated.carbon b/toolchain/check/testdata/class/fail_base_repeated.carbon index 4ac9da4230a1..02ef5e1f2920 100644 --- a/toolchain/check/testdata/class/fail_base_repeated.carbon +++ b/toolchain/check/testdata/class/fail_base_repeated.carbon @@ -8,26 +8,26 @@ base class B1 {} base class B2 {} class C { - base: B1; - // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+6]]:11: ERROR: Multiple `base` declarations in class. Multiple inheritance is not permitted. - // CHECK:STDERR: base: B2; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:11: Previous `base` declaration is here. - // CHECK:STDERR: base: B1; - // CHECK:STDERR: ^ - base: B2; + extend base: B1; + // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+6]]:18: ERROR: Multiple `base` declarations in class. Multiple inheritance is not permitted. + // CHECK:STDERR: extend base: B2; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:18: Previous `base` declaration is here. + // CHECK:STDERR: extend base: B1; + // CHECK:STDERR: ^ + extend base: B2; } class D { // TODO: Consider adding a custom diagnostic for this case. - base: B1; - // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+6]]:11: ERROR: Multiple `base` declarations in class. Multiple inheritance is not permitted. - // CHECK:STDERR: base: B1; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:11: Previous `base` declaration is here. - // CHECK:STDERR: base: B1; - // CHECK:STDERR: ^ - base: B1; + extend base: B1; + // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE+6]]:18: ERROR: Multiple `base` declarations in class. Multiple inheritance is not permitted. + // CHECK:STDERR: extend base: B1; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_base_repeated.carbon:[[@LINE-4]]:18: Previous `base` declaration is here. + // CHECK:STDERR: extend base: B1; + // CHECK:STDERR: ^ + extend base: B1; } // CHECK:STDOUT: --- fail_base_repeated.carbon @@ -63,21 +63,21 @@ class D { // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B1.ref: type = name_ref B1, file.%B1 -// CHECK:STDOUT: %.loc11_11.1: type = unbound_element_type C, B1 -// CHECK:STDOUT: %.loc11_11.2: = base_decl B1, element0 +// CHECK:STDOUT: %.loc11_18.1: type = unbound_element_type C, B1 +// CHECK:STDOUT: %.loc11_18.2: = base_decl B1, element0 // CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc11_11.2 +// CHECK:STDOUT: .base = %.loc11_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { // CHECK:STDOUT: %B1.ref.loc23: type = name_ref B1, file.%B1 -// CHECK:STDOUT: %.loc23_11.1: type = unbound_element_type D, B1 -// CHECK:STDOUT: %.loc23_11.2: = base_decl B1, element0 +// CHECK:STDOUT: %.loc23_18.1: type = unbound_element_type D, B1 +// CHECK:STDOUT: %.loc23_18.2: = base_decl B1, element0 // CHECK:STDOUT: %B1.ref.loc30: type = name_ref B1, file.%B1 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc23_11.2 +// CHECK:STDOUT: .base = %.loc23_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_base_unbound.carbon b/toolchain/check/testdata/class/fail_base_unbound.carbon index cdc679fca410..6c119c84b944 100644 --- a/toolchain/check/testdata/class/fail_base_unbound.carbon +++ b/toolchain/check/testdata/class/fail_base_unbound.carbon @@ -7,7 +7,7 @@ base class B {} class C { - base: B; + extend base: B; } // CHECK:STDERR: fail_base_unbound.carbon:[[@LINE+3]]:13: ERROR: Expression cannot be used as a value. @@ -32,7 +32,7 @@ let b: B = C.base; // CHECK:STDOUT: %C: type = class_type @C // CHECK:STDOUT: %B.ref: type = name_ref B, %B // CHECK:STDOUT: %C.ref: type = name_ref C, %C -// CHECK:STDOUT: %base.ref: = name_ref base, @C.%.loc10_10.2 +// CHECK:STDOUT: %base.ref: = name_ref base, @C.%.loc10_17.2 // CHECK:STDOUT: %b: B = bind_name b, // CHECK:STDOUT: } // CHECK:STDOUT: @@ -43,10 +43,10 @@ let b: B = C.base; // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B.ref: type = name_ref B, file.%B -// CHECK:STDOUT: %.loc10_10.1: type = unbound_element_type C, B -// CHECK:STDOUT: %.loc10_10.2: = base_decl B, element0 +// CHECK:STDOUT: %.loc10_17.1: type = unbound_element_type C, B +// CHECK:STDOUT: %.loc10_17.2: = base_decl B, element0 // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .base = %.loc10_10.2 +// CHECK:STDOUT: .base = %.loc10_17.2 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_emitter.h b/toolchain/diagnostics/diagnostic_emitter.h index 7ca4fcd6309b..e121d92bc15c 100644 --- a/toolchain/diagnostics/diagnostic_emitter.h +++ b/toolchain/diagnostics/diagnostic_emitter.h @@ -214,7 +214,9 @@ class DiagnosticEmitter { // A builder-pattern type to provide a fluent interface for constructing // a more complex diagnostic. See `DiagnosticEmitter::Build` for the // expected usage. - class DiagnosticBuilder { + // This is nodiscard to protect against accidentally building a diagnostic + // without emitting it. + class [[nodiscard]] DiagnosticBuilder { public: // DiagnosticBuilder is move-only and cannot be copied. DiagnosticBuilder(DiagnosticBuilder&&) noexcept = default; diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 3b0d94454bb4..2f739beda9b4 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -147,6 +147,7 @@ CARBON_DIAGNOSTIC_KIND(MissingObjectInMethodCall) // Class checking. CARBON_DIAGNOSTIC_KIND(BaseIsFinal) +CARBON_DIAGNOSTIC_KIND(BaseMissingExtend) CARBON_DIAGNOSTIC_KIND(BaseOutsideClass) CARBON_DIAGNOSTIC_KIND(BasePrevious) CARBON_DIAGNOSTIC_KIND(BaseRepeated) diff --git a/toolchain/lower/testdata/class/base.carbon b/toolchain/lower/testdata/class/base.carbon index c79a1af22fb3..2be21909ee17 100644 --- a/toolchain/lower/testdata/class/base.carbon +++ b/toolchain/lower/testdata/class/base.carbon @@ -9,8 +9,7 @@ base class Base { } class Derived { - // TODO: `extend base: Base;` - base: Base; + extend base: Base; var d: i32; } diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index 9486780abdfd..1ad52535ccf0 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -41,6 +41,7 @@ static auto TokenIsModifierOrIntroducer(Lex::TokenKind token_kind) -> bool { case Lex::TokenKind::Class: case Lex::TokenKind::Constraint: case Lex::TokenKind::Default: + case Lex::TokenKind::Extend: case Lex::TokenKind::Final: case Lex::TokenKind::Fn: case Lex::TokenKind::Impl: @@ -188,6 +189,21 @@ auto HandleDeclScopeLoop(Context& context) -> void { break; } + case Lex::TokenKind::Extend: { + // `extend` is considered a declaration modifier if it is followed by + // another modifier or an introducer. + if (TokenIsModifierOrIntroducer( + context.PositionKind(Lookahead::NextToken))) { + context.AddLeafNode(NodeKind::DeclModifierKeyword, context.Consume()); + saw_modifier = true; + } else { + // TODO: Treat this `extend` token as a declaration introducer + HandleUnrecognizedDecl(context, state.subtree_start); + return; + } + break; + } + // If we see a declaration introducer keyword token, replace the // placeholder node and switch to a state to parse the rest of the // declaration. We don't allow namespace or empty declarations here since diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 5e928ff4335d..9a9224869eb7 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -607,6 +607,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(DeclModifierKeyword, 0, CARBON_TOKEN(Abstract) CARBON_TOKEN(Base) CARBON_TOKEN(Default) + CARBON_TOKEN(Extend) CARBON_TOKEN(Final) CARBON_TOKEN(Impl) CARBON_TOKEN(Virtual)) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index ca9a9d0d7282..e9a8e0789d63 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -285,14 +285,16 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit) // // abstract // ^~~~~~~~ -// base +// base class // ^~~~ // default // ^~~~~~~ +// extend base +// ^~~~~~ // final // ^~~~~ -// override -// ^~~~~~~~ +// impl fn +// ^~~~ // private // ^~~~~~~ // protected diff --git a/toolchain/parse/testdata/class/base.carbon b/toolchain/parse/testdata/class/base.carbon index 93a551f42d0a..f9cd3003dab4 100644 --- a/toolchain/parse/testdata/class/base.carbon +++ b/toolchain/parse/testdata/class/base.carbon @@ -7,8 +7,10 @@ base class B {} class D { - base: B; + base: B1; + extend base: B2; base class Nested; + private base class PrivateNested; } // CHECK:STDOUT: - filename: base.carbon @@ -24,12 +26,22 @@ class D { // CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, // CHECK:STDOUT: {kind: 'BaseIntroducer', text: 'base'}, // CHECK:STDOUT: {kind: 'BaseColon', text: ':'}, -// CHECK:STDOUT: {kind: 'NameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'NameExpr', text: 'B1'}, // CHECK:STDOUT: {kind: 'BaseDecl', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'BaseIntroducer', text: 'base'}, +// CHECK:STDOUT: {kind: 'DeclModifierKeyword', text: 'extend'}, +// CHECK:STDOUT: {kind: 'BaseColon', text: ':'}, +// CHECK:STDOUT: {kind: 'NameExpr', text: 'B2'}, +// CHECK:STDOUT: {kind: 'BaseDecl', text: ';', subtree_size: 5}, // CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, // CHECK:STDOUT: {kind: 'DeclModifierKeyword', text: 'base'}, // CHECK:STDOUT: {kind: 'Name', text: 'Nested'}, // CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 4}, -// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'AccessModifierKeyword', text: 'private'}, +// CHECK:STDOUT: {kind: 'DeclModifierKeyword', text: 'base'}, +// CHECK:STDOUT: {kind: 'Name', text: 'PrivateNested'}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 22}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ]