mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Implement static var class fields (#7215)
This adds the `static` token to the lexer and parses it as a modifier. In check, `FullPatternStack::Kind::FieldDecl` is now used for both static and non-static vars. Static vars get treated basically the same as `NameBindingDecl`s. Global initialization is used for static var initializers. To make the necessary stack information available to `pattern_match.cpp`, the `full_pattern_stack` and `decl_introducer_state_stack` are now popped later in `handle_let_and_var.cpp`. In lowering, each class's body is checked for `VarStorage` insts and lowered the same as global vars.
This commit is contained in:
@@ -357,4 +357,18 @@ auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id,
|
||||
class_info.complete_type_witness_id = complete_type_witness_id;
|
||||
}
|
||||
|
||||
auto InNonStaticFieldDecl(Context& context) -> bool {
|
||||
return context.full_pattern_stack().IsCurrentKindClassScopeVarDecl() &&
|
||||
!context.decl_introducer_state_stack()
|
||||
.innermost()
|
||||
.modifier_set.HasAnyOf(KeywordModifierSet::Static);
|
||||
}
|
||||
|
||||
auto InStaticClassScopeVar(Context& context) -> bool {
|
||||
return context.full_pattern_stack().IsCurrentKindClassScopeVarDecl() &&
|
||||
context.decl_introducer_state_stack()
|
||||
.innermost()
|
||||
.modifier_set.HasAnyOf(KeywordModifierSet::Static);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -23,6 +23,12 @@ auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id,
|
||||
llvm::ArrayRef<SemIR::InstId> vtable_contents,
|
||||
llvm::ArrayRef<SemIR::InstId> body) -> void;
|
||||
|
||||
// Whether a non-static field decl is currently being checked.
|
||||
auto InNonStaticFieldDecl(Context& context) -> bool;
|
||||
|
||||
// Whether a static class var decl is currently being checked.
|
||||
auto InStaticClassScopeVar(Context& context) -> bool;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_CLASS_H_
|
||||
|
||||
@@ -31,7 +31,7 @@ struct DeclIntroducerState {
|
||||
Parse::NodeId
|
||||
ordered_modifier_node_ids[static_cast<int8_t>(ModifierOrder::Last) + 1] =
|
||||
{Parse::NodeId::None, Parse::NodeId::None, Parse::NodeId::None,
|
||||
Parse::NodeId::None, Parse::NodeId::None};
|
||||
Parse::NodeId::None, Parse::NodeId::None, Parse::NodeId::None};
|
||||
|
||||
// Invariant: contains just the modifiers represented by `saw_*_modifier`.
|
||||
KeywordModifierSet modifier_set = KeywordModifierSet();
|
||||
|
||||
@@ -40,8 +40,8 @@ class FullPatternStack {
|
||||
// A name-binding declaration, such as a `let` or `var` statement.
|
||||
NameBindingDecl,
|
||||
|
||||
// A non-static `var` field declaration inside a class.
|
||||
FieldDecl,
|
||||
// A `var` field declaration inside a class.
|
||||
ClassScopeVarDecl,
|
||||
|
||||
// The implicit parameter list of a function or impl declaration.
|
||||
ImplicitParamList,
|
||||
@@ -65,10 +65,9 @@ class FullPatternStack {
|
||||
// The kind of the current full-pattern.
|
||||
auto CurrentKind() const -> Kind { return kind_stack_.back(); }
|
||||
|
||||
// Whether the kind of the current full-pattern is a non-static class
|
||||
// `var` decl.
|
||||
auto IsCurrentKindFieldDecl() -> bool {
|
||||
return !empty() && CurrentKind() == Kind::FieldDecl;
|
||||
// Whether the kind of the current full-pattern is a class `var` decl.
|
||||
auto IsCurrentKindClassScopeVarDecl() -> bool {
|
||||
return !empty() && CurrentKind() == Kind::ClassScopeVarDecl;
|
||||
}
|
||||
|
||||
// Marks the start of a new full-pattern for a parameterized entity
|
||||
@@ -85,10 +84,9 @@ class FullPatternStack {
|
||||
bind_name_stack_.PushArray();
|
||||
}
|
||||
|
||||
// Marks the start of a new full-pattern for a non-staitc `var` field
|
||||
// declaration.
|
||||
auto PushFieldDecl() -> void {
|
||||
kind_stack_.push_back(Kind::FieldDecl);
|
||||
// Marks the start of a new full-pattern for a class `var` declaration.
|
||||
auto PushClassScopeVarDecl() -> void {
|
||||
kind_stack_.push_back(Kind::ClassScopeVarDecl);
|
||||
bind_name_stack_.PushArray();
|
||||
}
|
||||
|
||||
@@ -126,7 +124,8 @@ class FullPatternStack {
|
||||
|
||||
// Marks the start of the initializer for the current name binding decl.
|
||||
auto StartPatternInitializer() -> void {
|
||||
CARBON_CHECK(kind_stack_.back() == Kind::NameBindingDecl);
|
||||
CARBON_CHECK(kind_stack_.back() == Kind::ClassScopeVarDecl ||
|
||||
kind_stack_.back() == Kind::NameBindingDecl);
|
||||
for (auto& [name_id, inst_id] : bind_name_stack_.PeekArray()) {
|
||||
CARBON_CHECK(inst_id == SemIR::InstId::InitTombstone);
|
||||
auto& lookup_result = lookup_->Get(name_id);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "toolchain/check/global_init.h"
|
||||
|
||||
#include "toolchain/check/class.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
|
||||
@@ -57,4 +58,9 @@ auto GlobalInit::Finalize() -> void {
|
||||
.body_block_ids = {SemIR::InstBlockId::GlobalInit}}}));
|
||||
}
|
||||
|
||||
auto UseGlobalInit(Context& context) -> bool {
|
||||
return context.scope_stack().PeekIndex() == ScopeIndex::Package ||
|
||||
InStaticClassScopeVar(context);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -42,6 +42,12 @@ class GlobalInit {
|
||||
llvm::SmallVector<SemIR::InstId> block_;
|
||||
};
|
||||
|
||||
// Whether to use global initialization for the initializer of a declaration.
|
||||
//
|
||||
// This returns true for a declaration in package scope, or for a static
|
||||
// class field.
|
||||
auto UseGlobalInit(Context& context) -> bool;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_GLOBAL_INIT_H_
|
||||
|
||||
@@ -340,7 +340,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
}
|
||||
|
||||
case FullPatternStack::Kind::NameBindingDecl:
|
||||
case FullPatternStack::Kind::FieldDecl: {
|
||||
case FullPatternStack::Kind::ClassScopeVarDecl: {
|
||||
if (node_kind == Parse::NodeKind::FormBindingPattern) {
|
||||
return context.TODO(node_id, "support local form bindings");
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ static auto HandleIntroducer(Context& context, Parse::NodeId node_id) -> bool {
|
||||
context.pattern_block_stack().Push();
|
||||
if (context.scope_stack().TryGetCurrentScopeAs<SemIR::ClassDecl>() &&
|
||||
Kind == Lex::TokenKind::Var) {
|
||||
context.full_pattern_stack().PushFieldDecl();
|
||||
context.full_pattern_stack().PushClassScopeVarDecl();
|
||||
} else {
|
||||
context.full_pattern_stack().PushNameBindingDecl();
|
||||
}
|
||||
@@ -120,10 +120,18 @@ auto HandleParseNode(Context& context, Parse::VariablePatternId node_id)
|
||||
context, node_id,
|
||||
{.type_id = type_id, .subpattern_id = subpattern_id});
|
||||
break;
|
||||
case FullPatternStack::Kind::FieldDecl:
|
||||
// For class fields, a `FieldDecl` has already been created; do
|
||||
// not create a var pattern.
|
||||
return true;
|
||||
case FullPatternStack::Kind::ClassScopeVarDecl:
|
||||
if (InStaticClassScopeVar(context)) {
|
||||
// Handle static class fields the same as NameBindingDecls.
|
||||
pattern_id = AddInst<SemIR::VarPattern>(
|
||||
context, node_id,
|
||||
{.type_id = type_id, .subpattern_id = subpattern_id});
|
||||
break;
|
||||
} else {
|
||||
// For non-static class fields, a `FieldDecl` has already been
|
||||
// created; do not create a var pattern.
|
||||
return true;
|
||||
}
|
||||
case FullPatternStack::Kind::NotInEitherParamList:
|
||||
CARBON_FATAL("Unreachable");
|
||||
}
|
||||
@@ -146,9 +154,9 @@ static auto EndFullPattern(Context& context) -> void {
|
||||
}
|
||||
auto pattern_block_id = context.pattern_block_stack().Pop();
|
||||
|
||||
// For class fields, a `FieldDecl` has been created; skip creating a
|
||||
// name binding and var storage.
|
||||
if (context.full_pattern_stack().IsCurrentKindFieldDecl()) {
|
||||
// For non-static class fields, a `FieldDecl` has been created; skip
|
||||
// creating a name binding and var storage.
|
||||
if (InNonStaticFieldDecl(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,7 +171,7 @@ static auto EndFullPattern(Context& context) -> void {
|
||||
}
|
||||
|
||||
static auto StartPatternInitializer(Context& context) -> bool {
|
||||
if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
if (UseGlobalInit(context)) {
|
||||
context.global_init().Resume();
|
||||
}
|
||||
context.full_pattern_stack().StartPatternInitializer();
|
||||
@@ -171,7 +179,7 @@ static auto StartPatternInitializer(Context& context) -> bool {
|
||||
}
|
||||
|
||||
static auto EndPatternInitializer(Context& context) -> void {
|
||||
if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
if (UseGlobalInit(context)) {
|
||||
context.global_init().Suspend();
|
||||
}
|
||||
context.full_pattern_stack().EndPatternInitializer();
|
||||
@@ -202,7 +210,7 @@ auto HandleParseNode(Context& context,
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::VariableInitializerId node_id)
|
||||
-> bool {
|
||||
if (context.full_pattern_stack().IsCurrentKindFieldDecl()) {
|
||||
if (InNonStaticFieldDecl(context)) {
|
||||
context.TODO(node_id, "Field initializer");
|
||||
return false;
|
||||
}
|
||||
@@ -259,7 +267,7 @@ template <const Lex::TokenKind& IntroducerTokenKind,
|
||||
const Parse::NodeKind& InitializerNodeKind>
|
||||
static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo {
|
||||
DeclInfo decl_info = DeclInfo();
|
||||
bool is_field_decl = context.full_pattern_stack().IsCurrentKindFieldDecl();
|
||||
bool in_non_static_field_decl = InNonStaticFieldDecl(context);
|
||||
|
||||
// Handle the optional initializer.
|
||||
if (context.node_stack().PeekNextIs(InitializerNodeKind)) {
|
||||
@@ -281,7 +289,7 @@ static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo {
|
||||
|
||||
// A non-class variable declaration without an explicit initializer
|
||||
// is initialized by calling `(T as Core.DefaultOrUnformed).Op()`.
|
||||
if (!is_field_decl) {
|
||||
if (!in_non_static_field_decl) {
|
||||
if constexpr (IntroducerNodeKind == Parse::NodeKind::VariableIntroducer) {
|
||||
StartPatternInitializer(context);
|
||||
decl_info.init_id = MakeDefaultInit(context, node_id,
|
||||
@@ -290,9 +298,8 @@ static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
context.full_pattern_stack().PopFullPattern();
|
||||
|
||||
if (!is_field_decl) {
|
||||
if (!in_non_static_field_decl) {
|
||||
decl_info.pattern_id = context.node_stack().PopPattern();
|
||||
}
|
||||
|
||||
@@ -305,8 +312,7 @@ static auto HandleDecl(Context& context, Parse::NodeId node_id) -> DeclInfo {
|
||||
context.name_scopes()
|
||||
.GetInstIfValid(context.scope_stack().PeekNameScopeId())
|
||||
.second;
|
||||
decl_info.introducer =
|
||||
context.decl_introducer_state_stack().Pop<IntroducerTokenKind>();
|
||||
decl_info.introducer = context.decl_introducer_state_stack().innermost();
|
||||
CheckAccessModifiersOnDecl(context, decl_info.introducer, parent_scope_inst);
|
||||
|
||||
return decl_info;
|
||||
@@ -316,6 +322,8 @@ auto HandleParseNode(Context& context, Parse::LetDeclId node_id) -> bool {
|
||||
auto decl_info =
|
||||
HandleDecl<Lex::TokenKind::Let, Parse::NodeKind::LetIntroducer,
|
||||
Parse::NodeKind::LetInitializer>(context, node_id);
|
||||
context.full_pattern_stack().PopFullPattern();
|
||||
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Let>();
|
||||
|
||||
LimitModifiersOnDecl(
|
||||
context, decl_info.introducer,
|
||||
@@ -345,6 +353,8 @@ auto HandleParseNode(Context& context, Parse::AssociatedConstantDeclId node_id)
|
||||
Parse::NodeKind::AssociatedConstantIntroducer,
|
||||
Parse::NodeKind::AssociatedConstantInitializer>(
|
||||
context, node_id);
|
||||
context.full_pattern_stack().PopFullPattern();
|
||||
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Let>();
|
||||
|
||||
LimitModifiersOnDecl(
|
||||
context, decl_info.introducer,
|
||||
@@ -397,15 +407,18 @@ auto HandleParseNode(Context& context, Parse::VariableDeclId node_id) -> bool {
|
||||
HandleDecl<Lex::TokenKind::Var, Parse::NodeKind::VariableIntroducer,
|
||||
Parse::NodeKind::VariableInitializer>(context, node_id);
|
||||
|
||||
LimitModifiersOnDecl(
|
||||
context, decl_info.introducer,
|
||||
KeywordModifierSet::Access | KeywordModifierSet::Returned);
|
||||
LimitModifiersOnDecl(context, decl_info.introducer,
|
||||
KeywordModifierSet::Access |
|
||||
KeywordModifierSet::Returned |
|
||||
KeywordModifierSet::Static);
|
||||
|
||||
if (context.scope_stack().TryGetCurrentScopeAs<SemIR::ClassDecl>()) {
|
||||
return true;
|
||||
if (!InNonStaticFieldDecl(context)) {
|
||||
LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id);
|
||||
}
|
||||
|
||||
LocalPatternMatch(context, decl_info.pattern_id, decl_info.init_id);
|
||||
context.full_pattern_stack().PopFullPattern();
|
||||
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Var>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ static auto HandleModifier(Context& context, Parse::NodeId node_id,
|
||||
if (keyword.HasAnyOf(KeywordModifierSet::Access)) {
|
||||
order = ModifierOrder::Access;
|
||||
later_modifiers = KeywordModifierSet::Extern | KeywordModifierSet::Decl |
|
||||
KeywordModifierSet::Evaluation;
|
||||
KeywordModifierSet::Evaluation |
|
||||
KeywordModifierSet::Static;
|
||||
} else if (keyword.HasAnyOf(KeywordModifierSet::Extern)) {
|
||||
order = ModifierOrder::Extern;
|
||||
later_modifiers = KeywordModifierSet::Decl | KeywordModifierSet::Evaluation;
|
||||
@@ -60,6 +61,9 @@ static auto HandleModifier(Context& context, Parse::NodeId node_id,
|
||||
} else if (keyword.HasAnyOf(KeywordModifierSet::Evaluation)) {
|
||||
order = ModifierOrder::Evaluation;
|
||||
later_modifiers = KeywordModifierSet::None;
|
||||
} else if (keyword.HasAnyOf(KeywordModifierSet::Static)) {
|
||||
order = ModifierOrder::Static;
|
||||
later_modifiers = KeywordModifierSet::None;
|
||||
} else {
|
||||
CARBON_FATAL("Unexpected modifier keyword.");
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/check/class.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/handle.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
@@ -96,7 +97,7 @@ auto HandleParseNode(Context& context, Parse::TuplePatternId node_id) -> bool {
|
||||
llvm::SmallVector<SemIR::InstId> type_inst_ids;
|
||||
type_inst_ids.reserve(inst_block.size());
|
||||
for (auto inst : inst_block) {
|
||||
if (context.full_pattern_stack().IsCurrentKindFieldDecl()) {
|
||||
if (InNonStaticFieldDecl(context)) {
|
||||
CARBON_DIAGNOSTIC(FieldWithTuplePattern, Error,
|
||||
"found tuple pattern in class `var` decl");
|
||||
context.emitter().Emit(LocIdForDiagnostics::TokenOnly(node_id),
|
||||
|
||||
@@ -19,6 +19,7 @@ enum class ModifierOrder : int8_t {
|
||||
Extern,
|
||||
Extend,
|
||||
Decl,
|
||||
Static,
|
||||
Evaluation,
|
||||
Last = Evaluation
|
||||
};
|
||||
@@ -49,6 +50,9 @@ enum class ModifierOrder : int8_t {
|
||||
X(Returned) \
|
||||
X(Virtual) \
|
||||
\
|
||||
/* Static is standalone. */ \
|
||||
X(Static) \
|
||||
\
|
||||
/* Eval and MustEval are mutually exclusive. */ \
|
||||
X(Eval) \
|
||||
X(MustEval)
|
||||
@@ -158,11 +162,11 @@ static_assert(
|
||||
.HasAnyOf(KeywordModifierSet::Evaluation),
|
||||
"Order-related sets must not overlap");
|
||||
|
||||
#define CARBON_KEYWORD_MODIFIER_SET_IN_GROUP(Modifier) \
|
||||
static_assert((KeywordModifierSet::Access | KeywordModifierSet::Extern | \
|
||||
KeywordModifierSet::Extend | KeywordModifierSet::Decl | \
|
||||
KeywordModifierSet::Evaluation) \
|
||||
.HasAnyOf(KeywordModifierSet::Modifier), \
|
||||
#define CARBON_KEYWORD_MODIFIER_SET_IN_GROUP(Modifier) \
|
||||
static_assert((KeywordModifierSet::Access | KeywordModifierSet::Extern | \
|
||||
KeywordModifierSet::Extend | KeywordModifierSet::Decl | \
|
||||
KeywordModifierSet::Evaluation | KeywordModifierSet::Static) \
|
||||
.HasAnyOf(KeywordModifierSet::Modifier), \
|
||||
"Modifier missing from all modifier sets: " #Modifier);
|
||||
CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_IN_GROUP)
|
||||
#undef CARBON_KEYWORD_MODIFIER_SET_IN_GROUP
|
||||
|
||||
@@ -61,6 +61,8 @@ static auto ModifierOrderAsSet(ModifierOrder order) -> KeywordModifierSet {
|
||||
return KeywordModifierSet::Extend;
|
||||
case ModifierOrder::Decl:
|
||||
return KeywordModifierSet::Decl;
|
||||
case ModifierOrder::Static:
|
||||
return KeywordModifierSet::Static;
|
||||
case ModifierOrder::Evaluation:
|
||||
return KeywordModifierSet::Evaluation;
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
|
||||
}
|
||||
auto type_id = SemIR::ExtractScrutineeType(context.sem_ir(), pattern.type_id);
|
||||
|
||||
// Handle `var` decls in a class by creating a `FieldDecl`.
|
||||
if (context.full_pattern_stack().IsCurrentKindFieldDecl()) {
|
||||
// Handle non-static `var` decls in a class by creating a `FieldDecl`.
|
||||
if (InNonStaticFieldDecl(context)) {
|
||||
auto class_decl =
|
||||
context.scope_stack().TryGetCurrentScopeAs<SemIR::ClassDecl>();
|
||||
auto name_id = context.entity_names().Get(pattern.entity_name_id).name_id;
|
||||
|
||||
@@ -605,7 +605,7 @@ auto MatchContext::DoVarPreWorkImpl(State state,
|
||||
// TODO: Find a more efficient way to put these insts in the global_init
|
||||
// block (or drop the distinction between the global_init block and the
|
||||
// file scope?)
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
if (UseGlobalInit(context_)) {
|
||||
context_.global_init().Resume();
|
||||
}
|
||||
|
||||
@@ -625,7 +625,7 @@ auto MatchContext::DoVarPreWorkImpl(State state,
|
||||
{.lhs_id = storage_id, .rhs_id = init_id});
|
||||
}
|
||||
|
||||
if (context_.scope_stack().PeekIndex() == ScopeIndex::Package) {
|
||||
if (UseGlobalInit(context_)) {
|
||||
context_.global_init().Suspend();
|
||||
}
|
||||
return storage_id;
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
// 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
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/field/static.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/field/static.carbon
|
||||
|
||||
// --- static_field.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
class C {
|
||||
static var static_field: i32;
|
||||
static var static_field_with_init: i32 = 123;
|
||||
}
|
||||
|
||||
fn F() {
|
||||
C.static_field = 456;
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- private_static_field.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
private static var static_field: i32;
|
||||
|
||||
fn F() {
|
||||
Self.static_field = 123;
|
||||
}
|
||||
}
|
||||
|
||||
// --- fail_private_static_field_invalid_access.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
private static var static_field: i32;
|
||||
}
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_private_static_field_invalid_access.carbon:[[@LINE+7]]:3: error: cannot access private member `static_field` of type `C` [ClassInvalidMemberAccess]
|
||||
// CHECK:STDERR: C.static_field = 123;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_private_static_field_invalid_access.carbon:[[@LINE-7]]:22: note: declared here [ClassMemberDeclaration]
|
||||
// CHECK:STDERR: private static var static_field: i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
C.static_field = 123;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- static_field.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete]
|
||||
// CHECK:STDOUT: %T.67d: type = symbolic_binding T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.type.462: type = fn_type @T.as.DefaultOrUnformed.impl.Op, @T.as.DefaultOrUnformed.impl(%T.67d) [symbolic]
|
||||
// CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.b80: %T.as.DefaultOrUnformed.impl.Op.type.462 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness.89f: <witness> = impl_witness imports.%DefaultOrUnformed.impl_witness_table.725, @T.as.DefaultOrUnformed.impl(%i32) [concrete]
|
||||
// CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value %i32, (%DefaultOrUnformed.impl_witness.89f) [concrete]
|
||||
// CHECK:STDOUT: %int_123.fff: Core.IntLiteral = int_value 123 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.f67: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.8fd: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.f67 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.ac5: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.82d, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.a56: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.a56 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.ac5) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b91: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.9db: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b91, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.e93: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.0ac: <bound method> = bound_method %int_123.fff, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_123.f7f: %i32 = int_value 123 [concrete]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_456.010: Core.IntLiteral = int_value 456 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.ee2: <bound method> = bound_method %int_456.010, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a [concrete]
|
||||
// CHECK:STDOUT: %bound_method.4c0: <bound method> = bound_method %int_456.010, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_456.d17: %i32 = int_value 456 [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.dba: @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op.type (%T.as.DefaultOrUnformed.impl.Op.type.462) = import_ref Core//prelude/parts/default, loc{{\d+_\d+}}, loaded [symbolic = @T.as.DefaultOrUnformed.impl.%T.as.DefaultOrUnformed.impl.Op (constants.%T.as.DefaultOrUnformed.impl.Op.b80)]
|
||||
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table.725 = impl_witness_table (%Core.import_ref.dba), @T.as.DefaultOrUnformed.impl [concrete]
|
||||
// CHECK:STDOUT: %Core.import_ref.70a: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.f67) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.8fd)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.82d = impl_witness_table (%Core.import_ref.70a), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C {
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %static_field.patt: %pattern_type.7ce = ref_binding_pattern static_field [concrete]
|
||||
// CHECK:STDOUT: %static_field.var_patt: %pattern_type.7ce = var_pattern %static_field.patt [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %static_field.var: ref %i32 = var %static_field.var_patt [concrete]
|
||||
// CHECK:STDOUT: %i32.loc5: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %static_field: ref %i32 = ref_binding static_field, %static_field.var [concrete = %static_field.var]
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %static_field_with_init.patt: %pattern_type.7ce = ref_binding_pattern static_field_with_init [concrete]
|
||||
// CHECK:STDOUT: %static_field_with_init.var_patt: %pattern_type.7ce = var_pattern %static_field_with_init.patt [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %static_field_with_init.var: ref %i32 = var %static_field_with_init.var_patt [concrete]
|
||||
// CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %static_field_with_init: ref %i32 = ref_binding static_field_with_init, %static_field_with_init.var [concrete = %static_field_with_init.var]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357]
|
||||
// CHECK:STDOUT: complete_type_witness = %complete_type
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%C
|
||||
// CHECK:STDOUT: .static_field = %static_field
|
||||
// CHECK:STDOUT: .static_field_with_init = %static_field_with_init
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %static_field.ref: ref %i32 = name_ref static_field, @C.%static_field [concrete = @C.%static_field.var]
|
||||
// CHECK:STDOUT: %int_456: Core.IntLiteral = int_value 456 [concrete = constants.%int_456.010]
|
||||
// CHECK:STDOUT: %impl.elem0: %.9db = impl_witness_access constants.%ImplicitAs.impl_witness.ac5, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a]
|
||||
// CHECK:STDOUT: %bound_method.loc10_18.1: <bound method> = bound_method %int_456, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.ee2]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc10_18.2: <bound method> = bound_method %int_456, %specific_fn [concrete = constants.%bound_method.4c0]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc10_18.2(%int_456) [concrete = constants.%int_456.d17]
|
||||
// CHECK:STDOUT: %.loc10: init %i32 = converted %int_456, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_456.d17]
|
||||
// CHECK:STDOUT: assign %static_field.ref, %.loc10
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %DefaultOrUnformed.facet: %DefaultOrUnformed.type = facet_value constants.%i32, (constants.%DefaultOrUnformed.impl_witness.89f) [concrete = constants.%DefaultOrUnformed.facet]
|
||||
// CHECK:STDOUT: %.loc5_31.1: %DefaultOrUnformed.type = converted constants.%i32, %DefaultOrUnformed.facet [concrete = constants.%DefaultOrUnformed.facet]
|
||||
// CHECK:STDOUT: %as_type: type = facet_access_type %.loc5_31.1 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc5_31.2: type = converted %.loc5_31.1, %as_type [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %T.as.DefaultOrUnformed.impl.Op.call: init %i32 = call %T.as.DefaultOrUnformed.impl.Op.specific_fn()
|
||||
// CHECK:STDOUT: assign @C.%static_field.var, %T.as.DefaultOrUnformed.impl.Op.call
|
||||
// CHECK:STDOUT: %int_123: Core.IntLiteral = int_value 123 [concrete = constants.%int_123.fff]
|
||||
// CHECK:STDOUT: %impl.elem0: %.9db = impl_witness_access constants.%ImplicitAs.impl_witness.ac5, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f1a]
|
||||
// CHECK:STDOUT: %bound_method.loc6_10.1: <bound method> = bound_method %int_123, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.e93]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc6_10.2: <bound method> = bound_method %int_123, %specific_fn [concrete = constants.%bound_method.0ac]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc6_10.2(%int_123) [concrete = constants.%int_123.f7f]
|
||||
// CHECK:STDOUT: %.loc6: init %i32 = converted %int_123, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_123.f7f]
|
||||
// CHECK:STDOUT: assign @C.%static_field_with_init.var, %.loc6
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -215,6 +215,7 @@ CARBON_KEYWORD_TOKEN(Return, "return")
|
||||
CARBON_KEYWORD_TOKEN(Returned, "returned")
|
||||
CARBON_KEYWORD_TOKEN(SelfTypeIdentifier, "Self")
|
||||
CARBON_KEYWORD_TOKEN(SelfValueIdentifier, "self")
|
||||
CARBON_KEYWORD_TOKEN(Static, "static")
|
||||
// TODO: Although we provide a `str` type literal, it's not standardized.
|
||||
CARBON_KEYWORD_TOKEN(Str, "str")
|
||||
CARBON_KEYWORD_TOKEN(Template, "template")
|
||||
|
||||
@@ -115,28 +115,13 @@ auto FileContext::LowerDefinitions() -> void {
|
||||
// Lower global variable definitions.
|
||||
// TODO: Storing both a `constants_` array and a separate `global_variables_`
|
||||
// map is redundant.
|
||||
for (auto inst_id :
|
||||
sem_ir().inst_blocks().Get(sem_ir().top_inst_block_id())) {
|
||||
// Only `VarStorage` indicates a global variable declaration in the
|
||||
// top instruction block.
|
||||
if (auto var = sem_ir().insts().TryGetAs<SemIR::VarStorage>(inst_id)) {
|
||||
// Get the global variable declaration. We created this when lowering the
|
||||
// constant unless the variable is unnamed, in which case we need to
|
||||
// create it now.
|
||||
llvm::GlobalVariable* llvm_var = nullptr;
|
||||
if (auto const_id = sem_ir().constant_values().Get(inst_id);
|
||||
const_id.is_constant()) {
|
||||
llvm_var = cast<llvm::GlobalVariable>(GetConstant(const_id, inst_id));
|
||||
} else {
|
||||
// We should never be emitting a definition for a C++ global variable.
|
||||
llvm_var = BuildNonCppGlobalVariableDecl(*var);
|
||||
}
|
||||
LowerGlobalVariables(sem_ir().top_inst_block_id());
|
||||
|
||||
// Convert the declaration of this variable into a definition by adding an
|
||||
// initializer.
|
||||
global_variables_.Insert(inst_id, llvm_var);
|
||||
llvm_var->setInitializer(
|
||||
llvm::Constant::getNullValue(llvm_var->getValueType()));
|
||||
// Lower static class variable definitions.
|
||||
for (auto class_info : sem_ir().classes().values()) {
|
||||
auto inst_block_id = class_info.body_block_id;
|
||||
if (inst_block_id.has_value()) {
|
||||
LowerGlobalVariables(inst_block_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +249,33 @@ auto FileContext::GetOrCreateFunctionInfo(
|
||||
return result;
|
||||
}
|
||||
|
||||
auto FileContext::LowerGlobalVariables(SemIR::InstBlockId inst_block_id)
|
||||
-> void {
|
||||
for (auto inst_id : sem_ir().inst_blocks().Get(inst_block_id)) {
|
||||
// Only `VarStorage` indicates a global variable declaration in the
|
||||
// top instruction block.
|
||||
if (auto var = sem_ir().insts().TryGetAs<SemIR::VarStorage>(inst_id)) {
|
||||
// Get the global variable declaration. We created this when lowering the
|
||||
// constant unless the variable is unnamed, in which case we need to
|
||||
// create it now.
|
||||
llvm::GlobalVariable* llvm_var = nullptr;
|
||||
if (auto const_id = sem_ir().constant_values().Get(inst_id);
|
||||
const_id.is_constant()) {
|
||||
llvm_var = cast<llvm::GlobalVariable>(GetConstant(const_id, inst_id));
|
||||
} else {
|
||||
// We should never be emitting a definition for a C++ global variable.
|
||||
llvm_var = BuildNonCppGlobalVariableDecl(*var);
|
||||
}
|
||||
|
||||
// Convert the declaration of this variable into a definition by adding an
|
||||
// initializer.
|
||||
global_variables_.Insert(inst_id, llvm_var);
|
||||
llvm_var->setInitializer(
|
||||
llvm::Constant::getNullValue(llvm_var->getValueType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto FileContext::HandleReferencedCppFunction(clang::FunctionDecl* cpp_decl)
|
||||
-> llvm::Function* {
|
||||
// Create the LLVM function (`CodeGenModule::GetOrCreateLLVMFunction()`)
|
||||
|
||||
@@ -190,6 +190,9 @@ class FileContext {
|
||||
SemIR::SpecificId specific_id = SemIR::SpecificId::None) -> void;
|
||||
|
||||
private:
|
||||
// Lower global variables defined in `inst_block_id`.
|
||||
auto LowerGlobalVariables(SemIR::InstBlockId inst_block_id) -> void;
|
||||
|
||||
// Notes that a C++ function has been referenced for the first time, so we
|
||||
// should ask Clang to generate a definition for it if possible.
|
||||
auto HandleReferencedCppFunction(clang::FunctionDecl* cpp_decl)
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// 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
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/class/static.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/class/static.carbon
|
||||
|
||||
// --- static.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
static var static_field: i32;
|
||||
static var static_field_with_init: i32 = 123;
|
||||
}
|
||||
|
||||
fn F() {
|
||||
C.static_field = 456;
|
||||
C.static_field_with_init = 789;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ModuleID = 'static.carbon'
|
||||
// CHECK:STDOUT: source_filename = "static.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @_Cstatic_field.C.Main = global i32 0
|
||||
// CHECK:STDOUT: @_Cstatic_field_with_init.C.Main = global i32 0
|
||||
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: store i32 456, ptr @_Cstatic_field.C.Main, align 4, !dbg !7
|
||||
// CHECK:STDOUT: store i32 789, ptr @_Cstatic_field_with_init.C.Main, align 4, !dbg !8
|
||||
// CHECK:STDOUT: ret void, !dbg !9
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !10 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: store i32 poison, ptr @_Cstatic_field.C.Main, align 4, !dbg !11
|
||||
// CHECK:STDOUT: store i32 123, ptr @_Cstatic_field_with_init.C.Main, align 4, !dbg !12
|
||||
// CHECK:STDOUT: ret void, !dbg !13
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!0, !1}
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!2}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
// CHECK:STDOUT: !3 = !DIFile(filename: "static.carbon", directory: "")
|
||||
// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !5 = !DISubroutineType(types: !6)
|
||||
// CHECK:STDOUT: !6 = !{null}
|
||||
// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !8 = !DILocation(line: 10, column: 3, scope: !4)
|
||||
// CHECK:STDOUT: !9 = !DILocation(line: 8, column: 1, scope: !4)
|
||||
// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !3, type: !5, spFlags: DISPFlagDefinition, unit: !2)
|
||||
// CHECK:STDOUT: !11 = !DILocation(line: 4, column: 10, scope: !10)
|
||||
// CHECK:STDOUT: !12 = !DILocation(line: 5, column: 10, scope: !10)
|
||||
// CHECK:STDOUT: !13 = !DILocation(line: 0, scope: !10)
|
||||
@@ -360,6 +360,7 @@ CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(MustEval)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Override)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Private)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Protected)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Static)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Virtual)
|
||||
|
||||
CARBON_PARSE_NODE_KIND(ClassIntroducer)
|
||||
|
||||
+9
-1
@@ -11,6 +11,7 @@
|
||||
class Foo {
|
||||
var x: i32;
|
||||
var y: i32 = 0;
|
||||
static var z: i32;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +35,13 @@ class Foo {
|
||||
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
|
||||
// CHECK:STDOUT: {kind: 'IntLiteral', text: '0'},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 8},
|
||||
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 18},
|
||||
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
|
||||
// CHECK:STDOUT: {kind: 'StaticModifier', text: 'static'},
|
||||
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'z'},
|
||||
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
|
||||
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
|
||||
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
|
||||
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 7},
|
||||
// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 25},
|
||||
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
|
||||
// CHECK:STDOUT: ]
|
||||
|
||||
Reference in New Issue
Block a user