Add framework for the extern keyword. (#3755)

This doesn't add full support. I'm separating it out to make the effects
of the modifier changes clearer for review. I'm restructuring a little
with the expectation that we'll have some more categories of modifier
keywords in the future (similar to `extern`, these may not be in a "set"
such as access), and thus easily scaling up to a few more would be
useful.
This commit is contained in:
Jon Ross-Perkins
2024-03-08 16:01:12 +00:00
committed by GitHub
parent 56d70dc3a2
commit 096daecc57
21 changed files with 241 additions and 102 deletions
+36 -14
View File
@@ -22,15 +22,18 @@ enum class KeywordModifierSet : uint32_t {
Private = 1 << 0,
Protected = 1 << 1,
// Extern is standalone.
Extern = 1 << 2,
// At most one of these declaration modifiers allowed for a given
// declaration:
Abstract = 1 << 2,
Base = 1 << 3,
Default = 1 << 4,
Extend = 1 << 5,
Final = 1 << 6,
Impl = 1 << 7,
Virtual = 1 << 8,
Abstract = 1 << 3,
Base = 1 << 4,
Default = 1 << 5,
Extend = 1 << 6,
Final = 1 << 7,
Impl = 1 << 8,
Virtual = 1 << 9,
// Sets of modifiers:
Access = Private | Protected,
@@ -38,15 +41,25 @@ enum class KeywordModifierSet : uint32_t {
Method = Abstract | Impl | Virtual,
ImplDecl = Extend | Final,
Interface = Default | Final,
Decl = Class | Method | ImplDecl | Interface,
None = 0,
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Virtual)
};
inline auto operator!(KeywordModifierSet k) -> bool {
inline constexpr auto operator!(KeywordModifierSet k) -> bool {
return !static_cast<uint32_t>(k);
}
// The order of modifiers. Each of these corresponds to a group on
// KeywordModifierSet, and can be used as an array index.
enum class ModifierOrder : int8_t { Access, Extern, Decl, Last = Decl };
static_assert(!(KeywordModifierSet::Access & KeywordModifierSet::Extern) &&
!((KeywordModifierSet::Access | KeywordModifierSet::Extern) &
KeywordModifierSet::Decl),
"Order-related sets must not overlap");
// State stored for each declaration we are currently in: the kind of
// declaration and the keyword modifiers that apply to that declaration.
struct DeclState {
@@ -67,15 +80,24 @@ struct DeclState {
explicit DeclState(DeclKind decl_kind) : kind(decl_kind) {}
auto modifier_node_id(ModifierOrder order) -> Parse::NodeId {
return ordered_modifier_node_ids[static_cast<int8_t>(order)];
}
auto set_modifier_node_id(ModifierOrder order, Parse::NodeId node_id)
-> void {
ordered_modifier_node_ids[static_cast<int8_t>(order)] = node_id;
}
DeclKind kind;
// Nodes of modifiers on this declaration. `Invalid` if no modifier of that
// kind is present.
Parse::NodeId saw_access_modifier = Parse::NodeId::Invalid;
Parse::NodeId saw_decl_modifier = Parse::NodeId::Invalid;
// Nodes of modifiers on this declaration, in expected order. `Invalid` if no
// modifier of that kind is present.
Parse::NodeId
ordered_modifier_node_ids[static_cast<int8_t>(ModifierOrder::Decl) + 1] =
{Parse::NodeId::Invalid, Parse::NodeId::Invalid,
Parse::NodeId::Invalid};
// Invariant: contains just the modifiers represented by `saw_access_modifier`
// and `saw_other_modifier`.
// Invariant: contains just the modifiers represented by `saw_*_modifier`.
KeywordModifierSet modifier_set = KeywordModifierSet::None;
};
+2 -1
View File
@@ -31,7 +31,8 @@ auto HandleAlias(Context& context, Parse::AliasId /*parse_node*/) -> bool {
Lex::TokenKind::Alias);
auto modifiers = context.decl_state_stack().innermost().modifier_set;
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
context.decl_state_stack().Pop(DeclState::Alias);
+2 -1
View File
@@ -44,7 +44,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId parse_node)
auto modifiers = context.decl_state_stack().innermost().modifier_set;
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
auto inheritance_kind =
+11 -3
View File
@@ -101,17 +101,25 @@ static auto BuildFunctionDecl(Context& context,
// Process modifiers.
auto modifiers = DiagnoseModifiers(context, name_context.target_scope_id);
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
if (!!(modifiers & KeywordModifierSet::Extern)) {
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Extern),
"extern modifier");
}
if (!!(modifiers & KeywordModifierSet::Method)) {
context.TODO(context.decl_state_stack().innermost().saw_decl_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Decl),
"method modifier");
}
if (!!(modifiers & KeywordModifierSet::Interface)) {
// TODO: Once we are saving the modifiers for a function, add check that
// the function may only be defined if it is marked `default` or `final`.
context.TODO(context.decl_state_stack().innermost().saw_decl_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Decl),
"interface modifier");
}
context.decl_state_stack().Pop(DeclState::Fn);
+2 -1
View File
@@ -209,7 +209,8 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId parse_node)
// For an `extend impl` declaration, mark the impl as extending this `impl`.
if (!!(context.decl_state_stack().innermost().modifier_set &
KeywordModifierSet::Extend)) {
auto extend_node = context.decl_state_stack().innermost().saw_decl_modifier;
auto extend_node = context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Decl);
ExtendImpl(context, extend_node, parse_node, self_type_node, self_type_id,
params_node, constraint_type_id);
}
+2 -1
View File
@@ -45,7 +45,8 @@ static auto BuildInterfaceDecl(Context& context,
auto modifiers = context.decl_state_stack().innermost().modifier_set;
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
context.decl_state_stack().Pop(DeclState::Interface);
+4 -2
View File
@@ -87,11 +87,13 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool {
auto modifiers = context.decl_state_stack().innermost().modifier_set;
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
if (!!(modifiers & KeywordModifierSet::Interface)) {
context.TODO(context.decl_state_stack().innermost().saw_decl_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Decl),
"interface modifier");
}
context.decl_state_stack().Pop(DeclState::Let);
+36 -10
View File
@@ -37,26 +37,52 @@ static auto EmitNotAllowedWithDiagnostic(Context& context,
static auto HandleModifier(Context& context, Parse::NodeId parse_node,
KeywordModifierSet keyword) -> bool {
auto& s = context.decl_state_stack().innermost();
bool is_access = !!(keyword & KeywordModifierSet::Access);
auto& saw_modifier = is_access ? s.saw_access_modifier : s.saw_decl_modifier;
ModifierOrder order;
KeywordModifierSet later_modifiers;
if (!!(keyword & KeywordModifierSet::Access)) {
order = ModifierOrder::Access;
later_modifiers = KeywordModifierSet::Extern | KeywordModifierSet::Decl;
} else if (keyword == KeywordModifierSet::Extern) {
order = ModifierOrder::Extern;
later_modifiers = KeywordModifierSet::Decl;
} else {
order = ModifierOrder::Decl;
later_modifiers = KeywordModifierSet::None;
}
auto current_modifier_node_id = s.modifier_node_id(order);
if (!!(s.modifier_set & keyword)) {
EmitRepeatedDiagnostic(context, saw_modifier, parse_node);
} else if (saw_modifier.is_valid()) {
EmitNotAllowedWithDiagnostic(context, saw_modifier, parse_node);
} else if (is_access && s.saw_decl_modifier.is_valid()) {
EmitRepeatedDiagnostic(context, current_modifier_node_id, parse_node);
} else if (current_modifier_node_id.is_valid()) {
EmitNotAllowedWithDiagnostic(context, current_modifier_node_id, parse_node);
} else if (auto later_modifier_set = s.modifier_set & later_modifiers;
!!later_modifier_set) {
// At least one later modifier is present. Diagnose using the closest.
Parse::NodeId closest_later_modifier = Parse::NodeId::Invalid;
for (auto later_order = static_cast<int8_t>(order) + 1;
later_order <= static_cast<int8_t>(ModifierOrder::Last);
++later_order) {
if (s.ordered_modifier_node_ids[later_order] != Parse::NodeId::Invalid) {
closest_later_modifier = s.ordered_modifier_node_ids[later_order];
break;
}
}
CARBON_CHECK(closest_later_modifier.is_valid());
CARBON_DIAGNOSTIC(ModifierMustAppearBefore, Error,
"`{0}` must appear before `{1}`.", Lex::TokenKind,
Lex::TokenKind);
context.emitter()
.Build(parse_node, ModifierMustAppearBefore,
context.token_kind(parse_node),
context.token_kind(s.saw_decl_modifier))
.Note(s.saw_decl_modifier, ModifierPrevious,
context.token_kind(s.saw_decl_modifier))
context.token_kind(closest_later_modifier))
.Note(closest_later_modifier, ModifierPrevious,
context.token_kind(closest_later_modifier))
.Emit();
} else {
s.modifier_set |= keyword;
saw_modifier = parse_node;
s.set_modifier_node_id(order, parse_node);
}
return true;
}
+2 -1
View File
@@ -102,7 +102,8 @@ auto HandleVariableDecl(Context& context, Parse::VariableDeclId parse_node)
Lex::TokenKind::Var);
auto modifiers = context.decl_state_stack().innermost().modifier_set;
if (!!(modifiers & KeywordModifierSet::Access)) {
context.TODO(context.decl_state_stack().innermost().saw_access_modifier,
context.TODO(context.decl_state_stack().innermost().modifier_node_id(
ModifierOrder::Access),
"access modifier");
}
+26 -28
View File
@@ -4,6 +4,8 @@
#include "toolchain/check/modifiers.h"
#include "toolchain/check/decl_state.h"
namespace Carbon::Check {
static auto ReportNotAllowed(Context& context, Parse::NodeId modifier_node,
@@ -24,22 +26,16 @@ static auto ReportNotAllowed(Context& context, Parse::NodeId modifier_node,
diag.Emit();
}
auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
Lex::TokenKind decl_kind) -> void {
auto& s = context.decl_state_stack().innermost();
auto not_allowed = s.modifier_set & ~allowed;
if (!!(not_allowed & KeywordModifierSet::Access)) {
ReportNotAllowed(context, s.saw_access_modifier, decl_kind, "",
Parse::NodeId::Invalid);
not_allowed = not_allowed & ~KeywordModifierSet::Access;
s.saw_access_modifier = Parse::NodeId::Invalid;
// Returns the KeywordModifierSet corresponding to the ModifierOrder entry.
static auto ModifierOrderAsSet(ModifierOrder order) -> KeywordModifierSet {
switch (order) {
case ModifierOrder::Access:
return KeywordModifierSet::Access;
case ModifierOrder::Extern:
return KeywordModifierSet::Extern;
case ModifierOrder::Decl:
return KeywordModifierSet::Decl;
}
if (!!not_allowed) {
ReportNotAllowed(context, s.saw_decl_modifier, decl_kind, "",
Parse::NodeId::Invalid);
s.saw_decl_modifier = Parse::NodeId::Invalid;
}
s.modifier_set &= allowed;
}
auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
@@ -48,23 +44,26 @@ auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
Parse::NodeId context_node) -> void {
auto& s = context.decl_state_stack().innermost();
auto not_allowed = s.modifier_set & forbidden;
if (!!(not_allowed & KeywordModifierSet::Access)) {
ReportNotAllowed(context, s.saw_access_modifier, decl_kind, context_string,
context_node);
not_allowed = not_allowed & ~KeywordModifierSet::Access;
s.saw_access_modifier = Parse::NodeId::Invalid;
if (!not_allowed) {
return;
}
if (!!not_allowed) {
ReportNotAllowed(context, s.saw_decl_modifier, decl_kind, context_string,
context_node);
s.saw_decl_modifier = Parse::NodeId::Invalid;
for (auto order_index = 0;
order_index <= static_cast<int8_t>(ModifierOrder::Last); ++order_index) {
auto order = static_cast<ModifierOrder>(order_index);
if (!!(not_allowed & ModifierOrderAsSet(order))) {
ReportNotAllowed(context, s.modifier_node_id(order), decl_kind,
context_string, context_node);
s.set_modifier_node_id(order, Parse::NodeId::Invalid);
}
}
s.modifier_set = s.modifier_set & ~forbidden;
s.modifier_set &= ~forbidden;
}
// Returns the instruction that owns the given scope, or Invalid if the scope is
// not associated with an instruction.
auto GetScopeInstId(Context& context, SemIR::NameScopeId scope_id)
static auto GetScopeInstId(Context& context, SemIR::NameScopeId scope_id)
-> SemIR::InstId {
if (!scope_id.is_valid()) {
return SemIR::InstId::Invalid;
@@ -74,7 +73,7 @@ auto GetScopeInstId(Context& context, SemIR::NameScopeId scope_id)
// Returns the instruction that owns the given scope, or Invalid if the scope is
// not associated with an instruction.
auto GetScopeInst(Context& context, SemIR::NameScopeId scope_id)
static auto GetScopeInst(Context& context, SemIR::NameScopeId scope_id)
-> std::optional<SemIR::Inst> {
auto inst_id = GetScopeInstId(context, scope_id);
if (!inst_id.is_valid()) {
@@ -109,7 +108,6 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
", `private` is only allowed on class members and at file scope");
}
// Rules for abstract, virtual, and impl, which are only allowed in classes.
auto CheckMethodModifiersOnFunction(Context& context,
SemIR::NameScopeId target_scope_id)
-> void {
+8 -6
View File
@@ -25,12 +25,6 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
auto CheckMethodModifiersOnFunction(Context& context,
SemIR::NameScopeId target_scope_id) -> void;
// Reports a diagnostic (using `decl_kind`) if modifiers on this declaration are
// not in `allowed`. Updates the declaration state in
// `context.decl_state_stack()`.
auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
Lex::TokenKind decl_kind) -> void;
// Like `LimitModifiersOnDecl`, except says which modifiers are forbidden, and a
// `context_string` (and optional `context_node`) specifying the context in
// which those modifiers are forbidden.
@@ -40,6 +34,14 @@ auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
Parse::NodeId context_node = Parse::NodeId::Invalid)
-> void;
// Reports a diagnostic (using `decl_kind`) if modifiers on this declaration are
// not in `allowed`. Updates the declaration state in
// `context.decl_state_stack()`.
inline auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
Lex::TokenKind decl_kind) -> void {
ForbidModifiersOnDecl(context, ~allowed, decl_kind, "");
}
// Report a diagonostic if `default` and `final` modifiers are used on
// declarations where they are not allowed. Right now they are only allowed
// inside interfaces.
+7
View File
@@ -32,14 +32,21 @@ abstract base default final alias A = i32;
// CHECK:STDERR: ^~~~
impl alias B = i32;
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `extern` not allowed on `alias` declaration.
// CHECK:STDERR: extern alias C = i32;
// CHECK:STDERR: ^~~~~~
extern alias C = i32;
// CHECK:STDOUT: --- fail_modifiers.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .A = %A
// CHECK:STDOUT: .B = %B
// CHECK:STDOUT: .C = %C
// CHECK:STDOUT: }
// CHECK:STDOUT: %A: type = bind_alias A, i32 [template = i32]
// CHECK:STDOUT: %B: type = bind_alias B, i32 [template = i32]
// CHECK:STDOUT: %C: type = bind_alias C, i32 [template = i32]
// CHECK:STDOUT: }
// CHECK:STDOUT:
+13
View File
@@ -64,6 +64,11 @@ abstract protected class WrongOrder;
// CHECK:STDERR: ^~~~~~~~
abstract base class AbstractAndBase {}
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `extern` not allowed on `class` declaration.
// CHECK:STDERR: extern class ExternDefined {}
// CHECK:STDERR: ^~~~~~
extern class ExternDefined {}
// CHECK:STDOUT: --- fail_modifiers.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
@@ -74,6 +79,7 @@ abstract base class AbstractAndBase {}
// CHECK:STDOUT: %Virtual: type = class_type @Virtual [template]
// CHECK:STDOUT: %WrongOrder: type = class_type @WrongOrder [template]
// CHECK:STDOUT: %AbstractAndBase: type = class_type @AbstractAndBase [template]
// CHECK:STDOUT: %ExternDefined: type = class_type @ExternDefined [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -84,6 +90,7 @@ abstract base class AbstractAndBase {}
// CHECK:STDOUT: .Virtual = %Virtual.decl
// CHECK:STDOUT: .WrongOrder = %WrongOrder.decl
// CHECK:STDOUT: .AbstractAndBase = %AbstractAndBase.decl
// CHECK:STDOUT: .ExternDefined = %ExternDefined.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %DuplicatePrivate.decl: type = class_decl @DuplicatePrivate [template = constants.%DuplicatePrivate] {}
// CHECK:STDOUT: %TwoAccess.decl: type = class_decl @TwoAccess [template = constants.%TwoAccess] {}
@@ -91,6 +98,7 @@ abstract base class AbstractAndBase {}
// CHECK:STDOUT: %Virtual.decl: type = class_decl @Virtual [template = constants.%Virtual] {}
// CHECK:STDOUT: %WrongOrder.decl: type = class_decl @WrongOrder [template = constants.%WrongOrder] {}
// CHECK:STDOUT: %AbstractAndBase.decl: type = class_decl @AbstractAndBase [template = constants.%AbstractAndBase] {}
// CHECK:STDOUT: %ExternDefined.decl: type = class_decl @ExternDefined [template = constants.%ExternDefined] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @DuplicatePrivate;
@@ -114,3 +122,8 @@ abstract base class AbstractAndBase {}
// CHECK:STDOUT: .Self = constants.%AbstractAndBase
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @ExternDefined {
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%ExternDefined
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -70,6 +70,17 @@ base fn InvalidModifier();
// CHECK:STDERR: ^~~~~~~
default final virtual fn ModifiersConflict2() {}
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:1: ERROR: `extern` not allowed on `fn` declaration.
// CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:8: ERROR: `private` must appear before `extern`.
// CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: `extern` previously appeared here.
// CHECK:STDERR: extern private fn ExternOrderAndConflict() {}
// CHECK:STDERR: ^~~~~~
extern private fn ExternOrderAndConflict() {}
// CHECK:STDOUT: --- fail_modifiers.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -80,6 +91,7 @@ default final virtual fn ModifiersConflict2() {}
// CHECK:STDOUT: .ModifiersConflict = %ModifiersConflict
// CHECK:STDOUT: .InvalidModifier = %InvalidModifier
// CHECK:STDOUT: .ModifiersConflict2 = %ModifiersConflict2
// CHECK:STDOUT: .ExternOrderAndConflict = %ExternOrderAndConflict
// CHECK:STDOUT: }
// CHECK:STDOUT: %WrongOrder: <function> = fn_decl @WrongOrder [template] {}
// CHECK:STDOUT: %DuplicateVirtual: <function> = fn_decl @DuplicateVirtual [template] {}
@@ -87,6 +99,7 @@ default final virtual fn ModifiersConflict2() {}
// CHECK:STDOUT: %ModifiersConflict: <function> = fn_decl @ModifiersConflict [template] {}
// CHECK:STDOUT: %InvalidModifier: <function> = fn_decl @InvalidModifier [template] {}
// CHECK:STDOUT: %ModifiersConflict2: <function> = fn_decl @ModifiersConflict2 [template] {}
// CHECK:STDOUT: %ExternOrderAndConflict: <function> = fn_decl @ExternOrderAndConflict [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @WrongOrder();
@@ -110,3 +123,8 @@ default final virtual fn ModifiersConflict2() {}
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @ExternOrderAndConflict() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+40 -30
View File
@@ -4,45 +4,55 @@
//
// AUTOUPDATE
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+24]]:1: ERROR: `private` not allowed on `namespace` declaration.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+27]]:1: ERROR: `private` not allowed on `namespace` declaration.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+21]]:9: ERROR: `abstract` not allowed on `namespace` declaration.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+18]]:18: ERROR: `base` not allowed on declaration with `abstract`.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:9: `abstract` previously appeared here.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+12]]:23: ERROR: `default` not allowed on declaration with `abstract`.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:9: `abstract` previously appeared here.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:31: ERROR: `final` not allowed on declaration with `abstract`.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:9: `abstract` previously appeared here.
// CHECK:STDERR: private abstract base default final namespace Foo;
// CHECK:STDERR: ^~~~~~~~
private abstract base default final namespace Foo;
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+24]]:9: ERROR: `extern` not allowed on `namespace` declaration.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+21]]:16: ERROR: `abstract` not allowed on `namespace` declaration.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+18]]:25: ERROR: `base` not allowed on declaration with `abstract`.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:16: `abstract` previously appeared here.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+12]]:30: ERROR: `default` not allowed on declaration with `abstract`.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:16: `abstract` previously appeared here.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:38: ERROR: `final` not allowed on declaration with `abstract`.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:16: `abstract` previously appeared here.
// CHECK:STDERR: private extern abstract base default final namespace A;
// CHECK:STDERR: ^~~~~~~~
private extern abstract base default final namespace A;
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `impl` not allowed on `namespace` declaration.
// CHECK:STDERR: impl namespace Bar;
// CHECK:STDERR: impl namespace B;
// CHECK:STDERR: ^~~~
impl namespace Bar;
impl namespace B;
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `extern` not allowed on `namespace` declaration.
// CHECK:STDERR: extern namespace C;
// CHECK:STDERR: ^~~~~~
extern namespace C;
// CHECK:STDOUT: --- fail_modifiers.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Foo = %.loc31
// CHECK:STDOUT: .Bar = %.loc36
// CHECK:STDOUT: .A = %.loc34
// CHECK:STDOUT: .B = %.loc39
// CHECK:STDOUT: .C = %.loc44
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc31: <namespace> = namespace [template] {}
// CHECK:STDOUT: %.loc36: <namespace> = namespace [template] {}
// CHECK:STDOUT: %.loc34: <namespace> = namespace [template] {}
// CHECK:STDOUT: %.loc39: <namespace> = namespace [template] {}
// CHECK:STDOUT: %.loc44: <namespace> = namespace [template] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
+1
View File
@@ -160,6 +160,7 @@ CARBON_KEYWORD_TOKEN(Default, "default")
CARBON_KEYWORD_TOKEN(Destructor, "destructor")
CARBON_KEYWORD_TOKEN(Else, "else")
CARBON_KEYWORD_TOKEN(Extend, "extend")
CARBON_KEYWORD_TOKEN(Extern, "extern")
CARBON_KEYWORD_TOKEN(False, "false")
CARBON_KEYWORD_TOKEN(Final, "final")
CARBON_KEYWORD_TOKEN(Fn, "fn")
+1
View File
@@ -621,6 +621,7 @@ CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Abstract)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Base)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Default)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Extend)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Extern)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Final)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Impl)
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Private)
+11
View File
@@ -7,10 +7,12 @@
class A;
base class B;
abstract class C;
extern class D;
class A {}
base class B {}
abstract class C {}
extern class D {}
// CHECK:STDOUT: - filename: introducer.carbon
// CHECK:STDOUT: parse_tree: [
@@ -26,6 +28,10 @@ abstract class C {}
// CHECK:STDOUT: {kind: 'AbstractModifier', text: 'abstract'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'ExternModifier', text: 'extern'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'D'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'A'},
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
@@ -40,5 +46,10 @@ abstract class C {}
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'},
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 5},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'ExternModifier', text: 'extern'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'D'},
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
@@ -5,6 +5,7 @@
// AUTOUPDATE
fn F();
extern fn G();
// CHECK:STDOUT: - filename: basic.carbon
// CHECK:STDOUT: parse_tree: [
@@ -14,5 +15,11 @@ fn F();
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'ExternModifier', text: 'extern'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'G'},
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 6},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+9 -2
View File
@@ -4,8 +4,8 @@
//
// AUTOUPDATE
fn F() {
}
fn F() {}
extern fn G() {}
// CHECK:STDOUT: - filename: basic.carbon
// CHECK:STDOUT: parse_tree: [
@@ -16,5 +16,12 @@ fn F() {
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'ExternModifier', text: 'extern'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'G'},
// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('},
// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 6},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 7},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+3 -2
View File
@@ -4,7 +4,7 @@
//
// AUTOUPDATE
private abstract base default final namespace Foo;
private extern abstract base default final namespace Foo;
impl namespace Foo;
@@ -13,12 +13,13 @@ impl namespace Foo;
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'},
// CHECK:STDOUT: {kind: 'PrivateModifier', text: 'private'},
// CHECK:STDOUT: {kind: 'ExternModifier', text: 'extern'},
// CHECK:STDOUT: {kind: 'AbstractModifier', text: 'abstract'},
// CHECK:STDOUT: {kind: 'BaseModifier', text: 'base'},
// CHECK:STDOUT: {kind: 'DefaultModifier', text: 'default'},
// CHECK:STDOUT: {kind: 'FinalModifier', text: 'final'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'},
// CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 8},
// CHECK:STDOUT: {kind: 'Namespace', text: ';', subtree_size: 9},
// CHECK:STDOUT: {kind: 'NamespaceStart', text: 'namespace'},
// CHECK:STDOUT: {kind: 'ImplModifier', text: 'impl'},
// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'},