Parsing and basic checking for abstract class and base class. (#3385)

For now, we require the same introducer to be used each time a class is
declared, but see #3384.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2023-11-13 20:38:21 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent 49591d1360
commit 2715e2276e
23 changed files with 520 additions and 114 deletions
+12 -1
View File
@@ -539,9 +539,20 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
SemIR::InstId value_id,
ConversionTarget target) -> SemIR::InstId {
PendingBlock target_block(context);
auto& class_info = context.classes().Get(dest_type.class_id);
if (class_info.inheritance_kind == SemIR::Class::Abstract) {
CARBON_DIAGNOSTIC(ConstructionOfAbstractClass, Error,
"Cannot construct instance of abstract class. "
"Consider using `partial {0}` instead.",
std::string);
context.emitter().Emit(context.insts().Get(value_id).parse_node(),
ConstructionOfAbstractClass,
context.sem_ir().StringifyType(target.type_id));
return SemIR::InstId::BuiltinError;
}
auto dest_struct_type = context.insts().GetAs<SemIR::StructType>(
context.sem_ir().GetTypeAllowBuiltinTypes(
context.classes().Get(dest_type.class_id).object_representation_id));
class_info.object_representation_id));
// If we're trying to create a class value, form a temporary for the value to
// point to.
+45 -5
View File
@@ -18,17 +18,38 @@ auto HandleClassIntroducer(Context& context, Parse::Node parse_node) -> bool {
return true;
}
auto HandleAbstractModifier(Context& context, Parse::Node parse_node) -> bool {
context.node_stack().Push(parse_node);
return true;
}
auto HandleBaseModifier(Context& context, Parse::Node parse_node) -> bool {
context.node_stack().Push(parse_node);
return true;
}
static auto BuildClassDecl(Context& context)
-> std::tuple<SemIR::ClassId, SemIR::InstId> {
auto name_context = context.decl_name_stack().FinishName();
auto class_keyword =
auto introducer = context.node_stack().PeekParseNode();
bool abstract =
context.node_stack()
.PopForSoloParseNode<Parse::NodeKind::ClassIntroducer>();
.PopAndDiscardSoloParseNodeIf<Parse::NodeKind::AbstractModifier>();
bool base =
context.node_stack()
.PopAndDiscardSoloParseNodeIf<Parse::NodeKind::BaseModifier>();
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::ClassIntroducer>();
auto decl_block_id = context.inst_block_stack().Pop();
CARBON_CHECK(!(abstract && base)) << "Cannot be both `abstract` and `base`";
auto inheritance_kind = abstract ? SemIR::Class::Abstract
: base ? SemIR::Class::Base
: SemIR::Class::Final;
// Add the class declaration.
auto class_decl =
SemIR::ClassDecl{class_keyword, SemIR::ClassId::Invalid, decl_block_id};
SemIR::ClassDecl{introducer, SemIR::ClassId::Invalid, decl_block_id};
auto class_decl_id = context.AddInst(class_decl);
// Check whether this is a redeclaration.
@@ -39,6 +60,24 @@ static auto BuildClassDecl(Context& context)
context.insts().Get(existing_id).TryAs<SemIR::ClassDecl>()) {
// This is a redeclaration of an existing class.
class_decl.class_id = existing_class_decl->class_id;
auto& class_info = context.classes().Get(class_decl.class_id);
// The introducer kind must match the previous declaration.
// TODO: The rule here is not yet decided. See #3384.
if (class_info.inheritance_kind != inheritance_kind) {
CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducer, Error,
"Class redeclared with different inheritance kind.");
CARBON_DIAGNOSTIC(ClassRedeclarationDifferentIntroducerPrevious, Note,
"Previously declared here.");
context.emitter()
.Build(introducer, ClassRedeclarationDifferentIntroducer)
.Note(existing_class_decl->parse_node,
ClassRedeclarationDifferentIntroducerPrevious)
.Emit();
}
// TODO: Check that the generic parameter list agrees with the prior
// declaration.
} else {
// This is a redeclaration of something other than a class.
context.DiagnoseDuplicateName(name_context.parse_node, existing_id);
@@ -57,13 +96,14 @@ static auto BuildClassDecl(Context& context)
: SemIR::NameId::Invalid,
// `.self_type_id` depends on `class_id`, so is set below.
.self_type_id = SemIR::TypeId::Invalid,
.decl_id = class_decl_id});
.decl_id = class_decl_id,
.inheritance_kind = inheritance_kind});
// Build the `Self` type.
auto& class_info = context.classes().Get(class_decl.class_id);
class_info.self_type_id =
context.CanonicalizeType(context.AddInst(SemIR::ClassType{
class_keyword, context.GetBuiltinType(SemIR::BuiltinKind::TypeType),
introducer, context.GetBuiltinType(SemIR::BuiltinKind::TypeType),
class_decl.class_id}));
}
+3 -3
View File
@@ -62,7 +62,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
// error locations.
switch (auto context_parse_node_kind = context.parse_tree().node_kind(
context.node_stack().PeekParseNode())) {
case Parse::NodeKind::ReturnedSpecifier:
case Parse::NodeKind::ReturnedModifier:
case Parse::NodeKind::VariableIntroducer: {
// A `var` declaration at class scope introduces a field.
auto enclosing_class_decl = context.GetCurrentScopeAs<SemIR::ClassDecl>();
@@ -79,7 +79,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
}
SemIR::InstId value_id = SemIR::InstId::Invalid;
SemIR::TypeId value_type_id = cast_type_id;
if (context_parse_node_kind == Parse::NodeKind::ReturnedSpecifier) {
if (context_parse_node_kind == Parse::NodeKind::ReturnedModifier) {
CARBON_CHECK(!enclosing_class_decl) << "`returned var` at class scope";
value_id =
CheckReturnedVar(context, context.node_stack().PeekParseNode(),
@@ -108,7 +108,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
SemIR::BindName{name_node, value_type_id, name_id, value_id});
context.node_stack().Push(parse_node, bind_id);
if (context_parse_node_kind == Parse::NodeKind::ReturnedSpecifier) {
if (context_parse_node_kind == Parse::NodeKind::ReturnedModifier) {
RegisterReturnedVar(context, bind_id);
}
break;
+3 -4
View File
@@ -14,8 +14,7 @@ auto HandleReturnStatementStart(Context& context, Parse::Node parse_node)
return true;
}
auto HandleReturnVarSpecifier(Context& context, Parse::Node parse_node)
-> bool {
auto HandleReturnVarModifier(Context& context, Parse::Node parse_node) -> bool {
// No action, just a bracketing node.
context.node_stack().Push(parse_node);
return true;
@@ -31,10 +30,10 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool {
BuildReturnWithNoExpr(context, parse_node);
break;
case Parse::NodeKind::ReturnVarSpecifier:
case Parse::NodeKind::ReturnVarModifier:
// This is a `return var;` statement.
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::ReturnVarSpecifier>();
.PopAndDiscardSoloParseNode<Parse::NodeKind::ReturnVarModifier>();
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::ReturnStatementStart>();
BuildReturnVar(context, parse_node);
+2 -2
View File
@@ -15,7 +15,7 @@ auto HandleVariableIntroducer(Context& context, Parse::Node parse_node)
return true;
}
auto HandleReturnedSpecifier(Context& context, Parse::Node parse_node) -> bool {
auto HandleReturnedModifier(Context& context, Parse::Node parse_node) -> bool {
// No action, just a bracketing node.
context.node_stack().Push(parse_node);
return true;
@@ -54,7 +54,7 @@ auto HandleVariableDecl(Context& context, Parse::Node parse_node) -> bool {
// Pop the `returned` specifier if present.
context.node_stack()
.PopAndDiscardSoloParseNodeIf<Parse::NodeKind::ReturnedSpecifier>();
.PopAndDiscardSoloParseNodeIf<Parse::NodeKind::ReturnedModifier>();
// If there was an initializer, assign it to the storage.
if (has_init) {
+4 -2
View File
@@ -328,7 +328,9 @@ class NodeStack {
return IdKind::ClassId;
case Parse::NodeKind::Name:
return IdKind::NameId;
case Parse::NodeKind::AbstractModifier:
case Parse::NodeKind::ArrayExprSemi:
case Parse::NodeKind::BaseModifier:
case Parse::NodeKind::ClassIntroducer:
case Parse::NodeKind::CodeBlockStart:
case Parse::NodeKind::FunctionIntroducer:
@@ -338,9 +340,9 @@ class NodeStack {
case Parse::NodeKind::ParamListStart:
case Parse::NodeKind::ParenExprOrTupleLiteralStart:
case Parse::NodeKind::QualifiedDecl:
case Parse::NodeKind::ReturnedSpecifier:
case Parse::NodeKind::ReturnedModifier:
case Parse::NodeKind::ReturnStatementStart:
case Parse::NodeKind::ReturnVarSpecifier:
case Parse::NodeKind::ReturnVarModifier:
case Parse::NodeKind::SelfValueName:
case Parse::NodeKind::StructLiteralOrStructTypeLiteralStart:
case Parse::NodeKind::VariableInitializer:
+43
View File
@@ -0,0 +1,43 @@
// 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
abstract class Class {
var k: i32;
}
fn Make() -> Class {
// CHECK:STDERR: fail_abstract.carbon:[[@LINE+3]]:17: ERROR: Cannot construct instance of abstract class. Consider using `partial Class` instead.
// CHECK:STDERR: return {.k = 1};
// CHECK:STDERR: ^
return {.k = 1};
}
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.loc9: type = struct_type {.k: i32}
// CHECK:STDOUT: %.loc7: type = ptr_type {.k: i32}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file "fail_abstract.carbon" {
// CHECK:STDOUT: class_decl @Class, ()
// CHECK:STDOUT: %Class: type = class_type @Class
// CHECK:STDOUT: %Make: <function> = fn_decl @Make
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Class {
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Class, i32
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Class> = field k, member0
// CHECK:STDOUT: %k: <unbound field of class Class> = bind_name k, %.loc8_8.2
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .k = %k
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Make() -> %return: Class {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc15_16: i32 = int_literal 1
// CHECK:STDOUT: %.loc15_17: {.k: i32} = struct_literal (%.loc15_16)
// CHECK:STDOUT: return <error>
// CHECK:STDOUT: }
@@ -0,0 +1,139 @@
// 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
class A;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: base class A {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: class A;
// CHECK:STDERR: ^
base class A {}
class B;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: abstract class B {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: class B;
// CHECK:STDERR: ^
abstract class B {}
base class C;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: class C {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: base class C;
// CHECK:STDERR: ^
class C {}
base class D;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: abstract class D {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: base class D;
// CHECK:STDERR: ^
abstract class D {}
abstract class E;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: class E {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: abstract class E;
// CHECK:STDERR: ^
class E {}
abstract class F;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: base class F {}
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: abstract class F;
// CHECK:STDERR: ^
base class F {}
class G {}
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: abstract class G;
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-4]]:1: Previously declared here.
// CHECK:STDERR: class G {}
// CHECK:STDERR: ^
abstract class G;
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE+6]]:1: ERROR: Class redeclared with different inheritance kind.
// CHECK:STDERR: base class G;
// CHECK:STDERR: ^
// CHECK:STDERR: fail_redeclaration_introducer.carbon:[[@LINE-11]]:1: Previously declared here.
// CHECK:STDERR: class G {}
// CHECK:STDERR: ^
base class G;
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.loc14: type = struct_type {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file "fail_redeclaration_introducer.carbon" {
// CHECK:STDOUT: class_decl @A, ()
// CHECK:STDOUT: %A: type = class_type @A
// CHECK:STDOUT: class_decl @A, ()
// CHECK:STDOUT: class_decl @B, ()
// CHECK:STDOUT: %B: type = class_type @B
// CHECK:STDOUT: class_decl @B, ()
// CHECK:STDOUT: class_decl @C, ()
// CHECK:STDOUT: %C: type = class_type @C
// CHECK:STDOUT: class_decl @C, ()
// CHECK:STDOUT: class_decl @D, ()
// CHECK:STDOUT: %D: type = class_type @D
// CHECK:STDOUT: class_decl @D, ()
// CHECK:STDOUT: class_decl @E, ()
// CHECK:STDOUT: %E: type = class_type @E
// CHECK:STDOUT: class_decl @E, ()
// CHECK:STDOUT: class_decl @F, ()
// CHECK:STDOUT: %F: type = class_type @F
// CHECK:STDOUT: class_decl @F, ()
// CHECK:STDOUT: class_decl @G, ()
// CHECK:STDOUT: %G: type = class_type @G
// CHECK:STDOUT: class_decl @G, ()
// CHECK:STDOUT: class_decl @G, ()
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @A {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @B {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @D {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @E {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @F {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @G {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
@@ -0,0 +1,44 @@
// 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
class A;
base class B;
abstract class C;
class A {}
base class B {}
abstract class C {}
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.loc11: type = struct_type {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file "redeclaration_introducer.carbon" {
// CHECK:STDOUT: class_decl @A, ()
// CHECK:STDOUT: %A: type = class_type @A
// CHECK:STDOUT: class_decl @B, ()
// CHECK:STDOUT: %B: type = class_type @B
// CHECK:STDOUT: class_decl @C, ()
// CHECK:STDOUT: %C: type = class_type @C
// CHECK:STDOUT: class_decl @A, ()
// CHECK:STDOUT: class_decl @B, ()
// CHECK:STDOUT: class_decl @C, ()
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @A {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @B {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
@@ -132,8 +132,11 @@ CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable)
CARBON_DIAGNOSTIC_KIND(BreakOutsideLoop)
CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere)
CARBON_DIAGNOSTIC_KIND(ClassPreviousDefinition)
CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducer)
CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducerPrevious)
CARBON_DIAGNOSTIC_KIND(ClassRedefinition)
CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition)
CARBON_DIAGNOSTIC_KIND(ConstructionOfAbstractClass)
CARBON_DIAGNOSTIC_KIND(ContinueOutsideLoop)
CARBON_DIAGNOSTIC_KIND(CopyOfUncopyableType)
CARBON_DIAGNOSTIC_KIND(DereferenceOfNonPointer)
+19 -6
View File
@@ -18,6 +18,15 @@
namespace Carbon::Parse {
// An amount by which to look ahead of the current token. Lookahead should be
// used sparingly, and unbounded lookahead should be avoided.
//
// TODO: Decide whether we want to avoid lookahead altogether.
enum class Lookahead : int32_t {
CurrentToken = 0,
NextToken = 1,
};
// Context and shared functionality for parser handlers. See state.def for state
// documentation.
class Context {
@@ -197,14 +206,18 @@ class Context {
auto ConsumeListToken(NodeKind comma_kind, Lex::TokenKind close_kind,
bool already_has_error) -> ListTokenKind;
// Gets the kind of the next token to be consumed.
auto PositionKind() const -> Lex::TokenKind {
return tokens_->GetKind(*position_);
// Gets the kind of the next token to be consumed. If `lookahead` is
// provided, it specifies which token to inspect.
auto PositionKind(Lookahead lookahead = Lookahead::CurrentToken) const
-> Lex::TokenKind {
return tokens_->GetKind(position_[static_cast<int32_t>(lookahead)]);
}
// Tests whether the next token to be consumed is of the specified kind.
auto PositionIs(Lex::TokenKind kind) const -> bool {
return PositionKind() == kind;
// Tests whether the next token to be consumed is of the specified kind. If
// `lookahead` is provided, it specifies which token to inspect.
auto PositionIs(Lex::TokenKind kind,
Lookahead lookahead = Lookahead::CurrentToken) const -> bool {
return PositionKind(lookahead) == kind;
}
// Pops the state and keeps the value for inspection.
+79 -55
View File
@@ -22,75 +22,99 @@ static auto HandleUnrecognizedDecl(Context& context) -> void {
auto HandleDeclScopeLoop(Context& context) -> void {
// This maintains the current state unless we're at the end of the scope.
switch (auto position_kind = context.PositionKind()) {
auto position_kind = context.PositionKind();
switch (position_kind) {
case Lex::TokenKind::CloseCurlyBrace:
case Lex::TokenKind::EndOfFile: {
// This is the end of the scope, so the loop state ends.
context.PopAndDiscardState();
break;
return;
}
// `import` and `package` manage their packaging state.
case Lex::TokenKind::Import: {
context.PushState(State::Import);
break;
return;
}
case Lex::TokenKind::Package: {
context.PushState(State::Package);
return;
}
default: {
break;
}
default:
// Because a non-packaging keyword was encountered, packaging is complete.
// Misplaced packaging keywords may lead to this being re-triggered.
if (context.packaging_state() !=
Context::PackagingState::AfterNonPackagingDecl) {
if (!context.first_non_packaging_token().is_valid()) {
context.set_first_non_packaging_token(*context.position());
}
context.set_packaging_state(
Context::PackagingState::AfterNonPackagingDecl);
}
switch (position_kind) {
// Remaining keywords are only valid after imports are complete, and
// so all result in a `set_packaging_state` call. Note, this may not
// always be necessary but is probably cheaper than validating.
case Lex::TokenKind::Class: {
context.PushState(State::TypeIntroducerAsClass);
break;
}
case Lex::TokenKind::Constraint: {
context.PushState(State::TypeIntroducerAsNamedConstraint);
break;
}
case Lex::TokenKind::Fn: {
context.PushState(State::FunctionIntroducer);
break;
}
case Lex::TokenKind::Interface: {
context.PushState(State::TypeIntroducerAsInterface);
break;
}
case Lex::TokenKind::Namespace: {
context.PushState(State::Namespace);
break;
}
case Lex::TokenKind::Semi: {
context.AddLeafNode(NodeKind::EmptyDecl, context.Consume());
break;
}
case Lex::TokenKind::Var: {
context.PushState(State::VarAsDecl);
break;
}
case Lex::TokenKind::Let: {
context.PushState(State::Let);
break;
}
default: {
HandleUnrecognizedDecl(context);
break;
}
}
}
// Because a non-packaging keyword was encountered, packaging is complete.
// Misplaced packaging keywords may lead to this being re-triggered.
if (context.packaging_state() !=
Context::PackagingState::AfterNonPackagingDecl) {
if (!context.first_non_packaging_token().is_valid()) {
context.set_first_non_packaging_token(*context.position());
}
context.set_packaging_state(Context::PackagingState::AfterNonPackagingDecl);
}
// Remaining keywords are only valid after imports are complete, and so all
// result in a `set_packaging_state` call. Note, this may not always be
// necessary but is probably cheaper than validating.
switch (position_kind) {
case Lex::TokenKind::Abstract:
case Lex::TokenKind::Base: {
if (context.PositionIs(Lex::TokenKind::Class, Lookahead::NextToken)) {
context.PushState(State::TypeAfterIntroducerAsClass);
auto modifier_token = context.Consume();
auto class_token = context.Consume();
context.AddLeafNode(NodeKind::ClassIntroducer, class_token);
context.AddLeafNode(position_kind == Lex::TokenKind::Abstract
? NodeKind::AbstractModifier
: NodeKind::BaseModifier,
modifier_token);
return;
}
break;
}
case Lex::TokenKind::Class: {
context.PushState(State::TypeAfterIntroducerAsClass);
context.AddLeafNode(NodeKind::ClassIntroducer, context.Consume());
return;
}
case Lex::TokenKind::Constraint: {
context.PushState(State::TypeAfterIntroducerAsNamedConstraint);
context.AddLeafNode(NodeKind::NamedConstraintIntroducer,
context.Consume());
return;
}
case Lex::TokenKind::Fn: {
context.PushState(State::FunctionIntroducer);
return;
}
case Lex::TokenKind::Interface: {
context.PushState(State::TypeAfterIntroducerAsInterface);
context.AddLeafNode(NodeKind::InterfaceIntroducer, context.Consume());
return;
}
case Lex::TokenKind::Namespace: {
context.PushState(State::Namespace);
return;
}
case Lex::TokenKind::Semi: {
context.AddLeafNode(NodeKind::EmptyDecl, context.Consume());
return;
}
case Lex::TokenKind::Var: {
context.PushState(State::VarAsDecl);
return;
}
case Lex::TokenKind::Let: {
context.PushState(State::Let);
return;
}
default: {
break;
}
}
HandleUnrecognizedDecl(context);
}
} // namespace Carbon::Parse
+1 -1
View File
@@ -186,7 +186,7 @@ auto HandleStatementReturn(Context& context) -> void {
if (auto var_token = context.ConsumeIf(Lex::TokenKind::Var)) {
// `return var;`
context.AddLeafNode(NodeKind::ReturnVarSpecifier, *var_token);
context.AddLeafNode(NodeKind::ReturnVarModifier, *var_token);
} else if (!context.PositionIs(Lex::TokenKind::Semi)) {
// `return <expression>;`
context.PushState(State::Expr);
+9 -15
View File
@@ -6,31 +6,25 @@
namespace Carbon::Parse {
// Handles processing of a type's introducer.
static auto HandleTypeIntroducer(Context& context, NodeKind introducer_kind,
State after_params_state) -> void {
// Handles processing of a type declaration or definition after its introducer.
static auto HandleTypeAfterIntroducer(Context& context,
State after_params_state) -> void {
auto state = context.PopState();
context.AddLeafNode(introducer_kind, context.Consume());
state.state = after_params_state;
context.PushState(state);
context.PushState(State::DeclNameAndParamsAsOptional, state.token);
}
auto HandleTypeIntroducerAsClass(Context& context) -> void {
HandleTypeIntroducer(context, NodeKind::ClassIntroducer,
State::TypeAfterParamsAsClass);
auto HandleTypeAfterIntroducerAsClass(Context& context) -> void {
HandleTypeAfterIntroducer(context, State::TypeAfterParamsAsClass);
}
auto HandleTypeIntroducerAsInterface(Context& context) -> void {
HandleTypeIntroducer(context, NodeKind::InterfaceIntroducer,
State::TypeAfterParamsAsInterface);
auto HandleTypeAfterIntroducerAsInterface(Context& context) -> void {
HandleTypeAfterIntroducer(context, State::TypeAfterParamsAsInterface);
}
auto HandleTypeIntroducerAsNamedConstraint(Context& context) -> void {
HandleTypeIntroducer(context, NodeKind::NamedConstraintIntroducer,
State::TypeAfterParamsAsNamedConstraint);
auto HandleTypeAfterIntroducerAsNamedConstraint(Context& context) -> void {
HandleTypeAfterIntroducer(context, State::TypeAfterParamsAsNamedConstraint);
}
// Handles processing after params, deciding whether it's a declaration or
+1 -1
View File
@@ -19,7 +19,7 @@ static auto HandleVar(Context& context, State finish_state,
context.AddLeafNode(NodeKind::VariableIntroducer, context.Consume());
if (returned_token.is_valid()) {
context.AddLeafNode(NodeKind::ReturnedSpecifier, returned_token);
context.AddLeafNode(NodeKind::ReturnedModifier, returned_token);
}
context.PushState(State::PatternAsVariable);
+7 -4
View File
@@ -225,7 +225,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(LetDecl, LetIntroducer,
// `var` and `returned var`:
// VariableIntroducer
// _optional_ ReturnedSpecifier
// _optional_ ReturnedModifier
// _external_: PatternBinding
// _optional_ VariableInitializer
// _optional_ _external_: expression
@@ -236,7 +236,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(LetDecl, LetIntroducer,
CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableIntroducer, 0,
CARBON_TOKEN(Var)
CARBON_IF_ERROR(CARBON_TOKEN(Returned)))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnedSpecifier, 0, CARBON_TOKEN(Returned))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnedModifier, 0, CARBON_TOKEN(Returned))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(VariableInitializer, 0, CARBON_TOKEN(Equal))
CARBON_PARSE_NODE_KIND_BRACKET(VariableDecl, VariableIntroducer,
CARBON_TOKEN(Semi)
@@ -267,11 +267,11 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ContinueStatement, 1,
// `return`:
// ReturnStatementStart
// _optional_ ReturnVarSpecifier or _external_: expression
// _optional_ ReturnVarModifier or _external_: expression
// ReturnStatement
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnStatementStart, 0,
CARBON_TOKEN(Return))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnVarSpecifier, 0, CARBON_TOKEN(Var))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ReturnVarModifier, 0, CARBON_TOKEN(Var))
CARBON_PARSE_NODE_KIND_BRACKET(ReturnStatement, ReturnStatementStart,
CARBON_TOKEN(Semi)
CARBON_IF_ERROR(CARBON_TOKEN(Return)))
@@ -532,6 +532,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(StructTypeLiteral,
// `class`:
// ClassIntroducer
// _optional_ AbstractModifier or BaseModifier
// _external_: Name or QualifiedDecl
// ClassDefinitionStart
// _external_: declarations
@@ -541,6 +542,8 @@ CARBON_PARSE_NODE_KIND_BRACKET(StructTypeLiteral,
// ClassDefinitionStart and later nodes are removed and replaced by
// ClassDecl.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ClassIntroducer, 0, CARBON_TOKEN(Class))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(AbstractModifier, 0, CARBON_TOKEN(Abstract))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(BaseModifier, 0, CARBON_TOKEN(Base))
CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinitionStart, ClassIntroducer,
CARBON_TOKEN(OpenCurlyBrace))
CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinition, ClassDefinitionStart,
+15 -11
View File
@@ -283,14 +283,18 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit)
// Handles processing of a declaration scope. Things like fn, class, interface,
// and so on.
//
// class ...
// ^
// 1. TypeIntroducerAsClass
// class ...
// ^~~~~
// abstract class ...
// ^~~~~~~~~~~~~~
// base class ...
// ^~~~~~~~~~
// 1. TypeAfterIntroducerAsClass
// 2. DeclScopeLoop
//
// constraint ...
// ^
// 1. TypeIntroducerAsNamedConstraint
// constraint ...
// ^~~~~~~~~~
// 1. TypeAfterIntroducerAsNamedConstraint
// 2. DeclScopeLoop
//
// fn ...
@@ -298,9 +302,9 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit)
// 1. FunctionIntroducer
// 2. DeclScopeLoop
//
// interface ...
// ^
// 1. TypeIntroducerAsInterface
// interface ...
// ^~~~~~~~~
// 1. TypeAfterIntroducerAsInterface
// 2. DeclScopeLoop
//
// namespace ...
@@ -989,13 +993,13 @@ CARBON_PARSE_STATE(StatementWhileBlockFinish)
CARBON_PARSE_STATE_VARIANTS3(TypeDefinitionFinish, Class, Interface,
NamedConstraint)
// Handles processing of a type's introducer.
// Handles processing of a type after its introducer.
//
// class/interface/constraint ...
// ^~~~~~~~~~~~~~~~~~~~~~~~~~
// 1. DeclNameAndParamsAsOptional
// 2. TypeAfterParamsAs(Class|Interface|NamedConstraint)
CARBON_PARSE_STATE_VARIANTS3(TypeIntroducer, Class, Interface, NamedConstraint)
CARBON_PARSE_STATE_VARIANTS3(TypeAfterIntroducer, Class, Interface, NamedConstraint)
// Handles processing of a type after its optional parameters.
//
+31
View File
@@ -0,0 +1,31 @@
// 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
// TODO: Improve the diagnostic to say that the `abstract` modifier can't be
// used on an `interface`.
// CHECK:STDERR: fail_introducer.carbon:[[@LINE+3]]:1: ERROR: Unrecognized declaration introducer.
// CHECK:STDERR: abstract interface I;
// CHECK:STDERR: ^
abstract interface I;
// CHECK:STDERR: fail_introducer.carbon:[[@LINE+3]]:1: ERROR: Unrecognized declaration introducer.
// CHECK:STDERR: base fn F();
// CHECK:STDERR: ^
base fn F();
// CHECK:STDERR: fail_introducer.carbon:[[@LINE+3]]:1: ERROR: Unrecognized declaration introducer.
// CHECK:STDERR: abstract base class C;
// CHECK:STDERR: ^
abstract base class C;
// CHECK:STDOUT: - filename: fail_introducer.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'EmptyDecl', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'EmptyDecl', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'EmptyDecl', text: ';', has_error: yes},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+44
View File
@@ -0,0 +1,44 @@
// 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
class A;
base class B;
abstract class C;
class A {}
base class B {}
abstract class C {}
// CHECK:STDOUT: - filename: introducer.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'Name', text: 'A'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'BaseModifier', text: 'base'},
// CHECK:STDOUT: {kind: 'Name', text: 'B'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'AbstractModifier', text: 'abstract'},
// CHECK:STDOUT: {kind: 'Name', text: 'C'},
// CHECK:STDOUT: {kind: 'ClassDecl', text: ';', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'Name', text: 'A'},
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'},
// CHECK:STDOUT: {kind: 'BaseModifier', text: 'base'},
// CHECK:STDOUT: {kind: 'Name', text: 'B'},
// 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: 'AbstractModifier', text: 'abstract'},
// CHECK:STDOUT: {kind: 'Name', text: 'C'},
// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+1 -1
View File
@@ -29,7 +29,7 @@ fn foo() -> i32 {
// CHECK:STDOUT: {kind: 'ForHeader', text: ')', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'ReturnVarSpecifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnVarModifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3},
// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 5},
// CHECK:STDOUT: {kind: 'ForStatement', text: 'for', subtree_size: 9},
+1 -1
View File
@@ -20,7 +20,7 @@ fn F() {
// CHECK:STDOUT: {kind: 'ParamList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'ReturnVarSpecifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnVarModifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: 'return', has_error: yes, subtree_size: 3},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+2 -2
View File
@@ -20,7 +20,7 @@ fn F() -> String {
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnedSpecifier', text: 'returned'},
// CHECK:STDOUT: {kind: 'ReturnedModifier', text: 'returned'},
// CHECK:STDOUT: {kind: 'Name', text: 's'},
// CHECK:STDOUT: {kind: 'Literal', text: 'String'},
// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3},
@@ -28,7 +28,7 @@ fn F() -> String {
// CHECK:STDOUT: {kind: 'Literal', text: '"hello"'},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 8},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'ReturnVarSpecifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnVarModifier', text: 'var'},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 19},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+12
View File
@@ -62,6 +62,15 @@ struct Function : public Printable<Function> {
// A class.
struct Class : public Printable<Class> {
enum InheritanceKind {
// `abstract class`
Abstract,
// `base class`
Base,
// `class`
Final,
};
auto Print(llvm::raw_ostream& out) const -> void {
out << "{name: " << name_id;
out << "}";
@@ -80,6 +89,9 @@ struct Class : public Printable<Class> {
TypeId self_type_id;
// The first declaration of the class. This is a ClassDecl.
InstId decl_id = InstId::Invalid;
// The kind of inheritance that this class supports.
// TODO: The rules here are not yet decided. See #3384.
InheritanceKind inheritance_kind;
// The following members are set at the `{` of the class definition.