mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:14:42 +01:00
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:
co-authored by
Jon Ross-Perkins
parent
49591d1360
commit
2715e2276e
@@ -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.
|
||||
|
||||
@@ -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}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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: ]
|
||||
@@ -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
@@ -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
@@ -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
@@ -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: ''},
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user