From 62205763a5189e5c30d2d6a2240f6cc02a2d7546 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 8 Aug 2023 13:53:50 -0700 Subject: [PATCH] Add support for `&` operator. (#3055) Refactor type canonicalization so that we can reuse the same code for building a `T*` expression and for forming the type of an `&x` expression. Add basic computation of expression category in order to check that we only take the address of durable reference expressions. This is currently computed on demand rather than being tracked as part of the semantics node, but in most cases can be determined by looking at only a single expression, so caching it in the node doesn't seem worthwhile yet. This decision should be revisited if we start doing more complex category calculations. Also add trivial lowering support, but it doesn't work properly yet because lowering doesn't yet take the expression category into account. --- toolchain/diagnostics/diagnostic_kind.def | 2 + toolchain/lowering/lowering_context.cpp | 4 + toolchain/lowering/lowering_handle.cpp | 6 + .../testdata/pointer/address_of_field.carbon | 36 +++ .../lowering/testdata/pointer/basic.carbon | 26 ++ .../testdata/index/fail_malformed_expr.carbon | 11 +- toolchain/semantics/semantics_context.cpp | 144 +++++---- toolchain/semantics/semantics_context.h | 13 +- .../semantics/semantics_handle_operator.cpp | 40 ++- toolchain/semantics/semantics_ir.cpp | 90 +++++- toolchain/semantics/semantics_ir.h | 30 ++ toolchain/semantics/semantics_node.cpp | 2 +- toolchain/semantics/semantics_node.h | 3 + toolchain/semantics/semantics_node_kind.def | 1 + .../testdata/pointer/address_of_lvalue.carbon | 241 ++++++++++++++ .../semantics/testdata/pointer/basic.carbon | 12 +- .../pointer/fail_address_of_value.carbon | 298 ++++++++++++++++++ 17 files changed, 879 insertions(+), 80 deletions(-) create mode 100644 toolchain/lowering/testdata/pointer/address_of_field.carbon create mode 100644 toolchain/lowering/testdata/pointer/basic.carbon create mode 100644 toolchain/semantics/testdata/pointer/address_of_lvalue.carbon create mode 100644 toolchain/semantics/testdata/pointer/fail_address_of_value.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 0333a64f0bf4..a39ce494c7b8 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -96,6 +96,8 @@ CARBON_DIAGNOSTIC_KIND(ParametersRequiredByDeduced) CARBON_DIAGNOSTIC_KIND(SemanticsTodo) +CARBON_DIAGNOSTIC_KIND(AddressOfEphemeralReference) +CARBON_DIAGNOSTIC_KIND(AddressOfNonReference) CARBON_DIAGNOSTIC_KIND(NameNotFound) CARBON_DIAGNOSTIC_KIND(NameDeclarationDuplicate) CARBON_DIAGNOSTIC_KIND(NameDeclarationPrevious) diff --git a/toolchain/lowering/lowering_context.cpp b/toolchain/lowering/lowering_context.cpp index 4cefded98922..f482d70920cf 100644 --- a/toolchain/lowering/lowering_context.cpp +++ b/toolchain/lowering/lowering_context.cpp @@ -144,6 +144,10 @@ auto LoweringContext::BuildType(SemanticsNodeId node_id) -> llvm::Type* { auto node = semantics_ir_->GetNode(node_id); switch (node.kind()) { + case SemanticsNodeKind::ConstType: + return GetType(node.GetAsConstType()); + case SemanticsNodeKind::PointerType: + return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0); case SemanticsNodeKind::StructType: { auto refs = semantics_ir_->GetNodeBlock(node.GetAsStructType()); llvm::SmallVector subtypes; diff --git a/toolchain/lowering/lowering_handle.cpp b/toolchain/lowering/lowering_handle.cpp index 7bba6db1aeb2..ebea588284d7 100644 --- a/toolchain/lowering/lowering_handle.cpp +++ b/toolchain/lowering/lowering_handle.cpp @@ -18,6 +18,12 @@ auto LoweringHandleCrossReference(LoweringFunctionContext& /*context*/, CARBON_FATAL() << "TODO: Add support: " << node; } +auto LoweringHandleAddressOf(LoweringFunctionContext& context, + SemanticsNodeId node_id, SemanticsNode node) + -> void { + context.SetLocal(node_id, context.GetLocal(node.GetAsAddressOf())); +} + auto LoweringHandleAssign(LoweringFunctionContext& context, SemanticsNodeId /*node_id*/, SemanticsNode node) -> void { diff --git a/toolchain/lowering/testdata/pointer/address_of_field.carbon b/toolchain/lowering/testdata/pointer/address_of_field.carbon new file mode 100644 index 000000000000..858b31d95b1f --- /dev/null +++ b/toolchain/lowering/testdata/pointer/address_of_field.carbon @@ -0,0 +1,36 @@ +// 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 + +fn G(p: i32*); + +fn F() { + var s: {.a: i32, .b: i32} = {.a = 1, .b = 2}; + // TODO: The lowering here is incorrect: we're incorrectly loading `s.b`. + G(&s.b); +} + +// CHECK:STDOUT: ; ModuleID = 'address_of_field.carbon' +// CHECK:STDOUT: source_filename = "address_of_field.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: %StructLiteralType = type { i32, i32 } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @G(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @F() { +// CHECK:STDOUT: %var = alloca %StructLiteralType, align 8 +// CHECK:STDOUT: %StructLiteralValue = alloca %StructLiteralType, align 8 +// CHECK:STDOUT: %a = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 0 +// CHECK:STDOUT: store i32 1, ptr %a, align 4 +// CHECK:STDOUT: %b = getelementptr inbounds %StructLiteralType, ptr %StructLiteralValue, i32 0, i32 1 +// CHECK:STDOUT: store i32 2, ptr %b, align 4 +// CHECK:STDOUT: %1 = load %StructLiteralType, ptr %StructLiteralValue, align 4 +// CHECK:STDOUT: store %StructLiteralType %1, ptr %var, align 4 +// CHECK:STDOUT: %b1 = getelementptr inbounds %StructLiteralType, ptr %var, i32 0, i32 1 +// CHECK:STDOUT: %2 = load ptr, ptr %b1, align 8 +// CHECK:STDOUT: call void @G(ptr %2) +// CHECK:STDOUT: %TupleLiteralValue = alloca {}, align 8 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/lowering/testdata/pointer/basic.carbon b/toolchain/lowering/testdata/pointer/basic.carbon new file mode 100644 index 000000000000..e5565ff378f0 --- /dev/null +++ b/toolchain/lowering/testdata/pointer/basic.carbon @@ -0,0 +1,26 @@ +// 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 + +fn G(p: i32*); + +fn F() { + var n: i32 = 0; + G(&n); +} + +// CHECK:STDOUT: ; ModuleID = 'basic.carbon' +// CHECK:STDOUT: source_filename = "basic.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @G(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @F() { +// CHECK:STDOUT: %var = alloca i32, align 4 +// CHECK:STDOUT: store i32 0, ptr %var, align 4 +// CHECK:STDOUT: %1 = load ptr, ptr %var, align 8 +// CHECK:STDOUT: call void @G(ptr %1) +// CHECK:STDOUT: %TupleLiteralValue = alloca {}, align 8 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/parser/testdata/index/fail_malformed_expr.carbon b/toolchain/parser/testdata/index/fail_malformed_expr.carbon index c116fbcc0aa9..3a9ff006a4dd 100644 --- a/toolchain/parser/testdata/index/fail_malformed_expr.carbon +++ b/toolchain/parser/testdata/index/fail_malformed_expr.carbon @@ -3,6 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // AUTOUPDATE + +// CHECK:STDERR: fail_malformed_expr.carbon:[[@LINE+3]]:17: Unexpected tokens before `]`. +// CHECK:STDERR: var v: i32 = t[0,]; +// CHECK:STDERR: ^ +var v: i32 = t[0,]; + // CHECK:STDOUT: [ // CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, // CHECK:STDOUT: {kind: 'Name', text: 'v'}, @@ -16,8 +22,3 @@ // CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 10}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] - -// CHECK:STDERR: fail_malformed_expr.carbon:[[@LINE+3]]:17: Unexpected tokens before `]`. -// CHECK:STDERR: var v: i32 = t[0,]; -// CHECK:STDERR: ^ -var v: i32 = t[0,]; diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp index 884431412c40..9dc191e9748c 100644 --- a/toolchain/semantics/semantics_context.cpp +++ b/toolchain/semantics/semantics_context.cpp @@ -483,81 +483,104 @@ auto SemanticsContext::CanonicalizeTypeImpl( return type_id; } +// Compute a fingerprint for a tuple type, for use as a key in a folding set. +static auto ProfileTupleType(const llvm::SmallVector& type_ids, + llvm::FoldingSetNodeID& canonical_id) -> void { + for (const auto& type_id : type_ids) { + canonical_id.AddInteger(type_id.index); + } +} + +// Compute a fingerprint for a type, for use as a key in a folding set. +static auto ProfileType(SemanticsContext& semantics_context, SemanticsNode node, + llvm::FoldingSetNodeID& canonical_id) -> void { + switch (node.kind()) { + case SemanticsNodeKind::Builtin: + canonical_id.AddInteger(node.GetAsBuiltin().AsInt()); + break; + case SemanticsNodeKind::CrossReference: { + // TODO: Cross-references should be canonicalized by looking at their + // target rather than treating them as new unique types. + auto [xref_id, node_id] = node.GetAsCrossReference(); + canonical_id.AddInteger(xref_id.index); + canonical_id.AddInteger(node_id.index); + break; + } + case SemanticsNodeKind::ConstType: + canonical_id.AddInteger( + semantics_context.GetUnqualifiedType(node.GetAsConstType()).index); + break; + case SemanticsNodeKind::PointerType: + canonical_id.AddInteger(node.GetAsPointerType().index); + break; + case SemanticsNodeKind::StructType: { + auto refs = + semantics_context.semantics_ir().GetNodeBlock(node.GetAsStructType()); + for (const auto& ref_id : refs) { + auto ref = semantics_context.semantics_ir().GetNode(ref_id); + auto [name_id, type_id] = ref.GetAsStructTypeField(); + canonical_id.AddInteger(name_id.index); + canonical_id.AddInteger(type_id.index); + } + break; + } + case SemanticsNodeKind::StubReference: { + // We rely on stub references not referring to each other to ensure we + // only recurse once here. + auto inner = + semantics_context.semantics_ir().GetNode(node.GetAsStubReference()); + CARBON_CHECK(inner.kind() != SemanticsNodeKind::StubReference) + << "A stub reference should never refer to another stub reference."; + ProfileType(semantics_context, inner, canonical_id); + break; + } + case SemanticsNodeKind::TupleType: + ProfileTupleType( + semantics_context.semantics_ir().GetTypeBlock(node.GetAsTupleType()), + canonical_id); + break; + default: + CARBON_FATAL() << "Unexpected type node " << node; + } +} + +auto SemanticsContext::CanonicalizeTypeAndAddNodeIfNew(SemanticsNode node) + -> SemanticsTypeId { + auto profile_node = [&](llvm::FoldingSetNodeID& canonical_id) { + ProfileType(*this, node, canonical_id); + }; + auto make_node = [&] { return AddNode(node); }; + return CanonicalizeTypeImpl(node.kind(), profile_node, make_node); +} + auto SemanticsContext::CanonicalizeType(SemanticsNodeId node_id) -> SemanticsTypeId { - auto node = semantics_ir_->GetNode(node_id); - if (node.kind() == SemanticsNodeKind::StubReference) { - node_id = node.GetAsStubReference(); - CARBON_CHECK(semantics_ir_->GetNode(node_id).kind() != - SemanticsNodeKind::StubReference) - << "Stub reference should not point to another stub reference"; - } - auto it = canonical_types_.find(node_id); if (it != canonical_types_.end()) { return it->second; } - switch (node.kind()) { - case SemanticsNodeKind::Builtin: - case SemanticsNodeKind::CrossReference: { - // TODO: Cross-references should be canonicalized by looking at their - // target rather than treating them as new unique types. - auto type_id = semantics_ir_->AddType(node_id); - CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second); - return type_id; - } - case SemanticsNodeKind::ConstType: { - return CanonicalizeTypeImpl( - node.kind(), node_id, [&](llvm::FoldingSetNodeID& canonical_id) { - canonical_id.AddInteger( - GetUnqualifiedType(node.GetAsConstType()).index); - }); - } - case SemanticsNodeKind::PointerType: { - return CanonicalizeTypeImpl( - node.kind(), node_id, [&](llvm::FoldingSetNodeID& canonical_id) { - canonical_id.AddInteger(node.GetAsPointerType().index); - }); - } - case SemanticsNodeKind::StructType: - case SemanticsNodeKind::TupleType: { - CARBON_FATAL() << "Type should have been canonizalized when created: " - << node; - } - default: { - CARBON_FATAL() << "Unexpected non-canonical type node " << node; - } - } + auto node = semantics_ir_->GetNode(node_id); + auto profile_node = [&](llvm::FoldingSetNodeID& canonical_id) { + ProfileType(*this, node, canonical_id); + }; + auto make_node = [&] { return node_id; }; + return CanonicalizeTypeImpl(node.kind(), profile_node, make_node); } auto SemanticsContext::CanonicalizeStructType(ParseTree::Node parse_node, SemanticsNodeBlockId refs_id) -> SemanticsTypeId { - auto profile_struct = [&](llvm::FoldingSetNodeID& canonical_id) { - auto refs = semantics_ir_->GetNodeBlock(refs_id); - for (const auto& ref_id : refs) { - auto ref = semantics_ir_->GetNode(ref_id); - auto [name_id, type_id] = ref.GetAsStructTypeField(); - canonical_id.AddInteger(name_id.index); - canonical_id.AddInteger(type_id.index); - } - }; - auto make_struct_node = [&] { - return AddNode(SemanticsNode::StructType::Make( - parse_node, SemanticsTypeId::TypeType, refs_id)); - }; - return CanonicalizeTypeImpl(SemanticsNodeKind::StructType, profile_struct, - make_struct_node); + return CanonicalizeTypeAndAddNodeIfNew(SemanticsNode::StructType::Make( + parse_node, SemanticsTypeId::TypeType, refs_id)); } auto SemanticsContext::CanonicalizeTupleType( ParseTree::Node parse_node, llvm::SmallVector&& type_ids) -> SemanticsTypeId { + // Defer allocating a SemanticsTypeBlockId until we know this is a new type. auto profile_tuple = [&](llvm::FoldingSetNodeID& canonical_id) { - for (const auto& type_id : type_ids) { - canonical_id.AddInteger(type_id.index); - } + ProfileTupleType(type_ids, canonical_id); }; auto make_tuple_node = [&] { auto type_block_id = semantics_ir_->AddTypeBlock(); @@ -570,6 +593,13 @@ auto SemanticsContext::CanonicalizeTupleType( make_tuple_node); } +auto SemanticsContext::GetPointerType(ParseTree::Node parse_node, + SemanticsTypeId pointee_type_id) + -> SemanticsTypeId { + return CanonicalizeTypeAndAddNodeIfNew(SemanticsNode::PointerType::Make( + parse_node, SemanticsTypeId::TypeType, pointee_type_id)); +} + auto SemanticsContext::GetUnqualifiedType(SemanticsTypeId type_id) -> SemanticsTypeId { SemanticsNode type_node = diff --git a/toolchain/semantics/semantics_context.h b/toolchain/semantics/semantics_context.h index 6e80050cf629..5146542286c8 100644 --- a/toolchain/semantics/semantics_context.h +++ b/toolchain/semantics/semantics_context.h @@ -153,6 +153,10 @@ class SemanticsContext { llvm::SmallVector&& type_ids) -> SemanticsTypeId; + // Returns a pointer type whose pointee type is `pointee_type_id`. + auto GetPointerType(ParseTree::Node parse_node, + SemanticsTypeId pointee_type_id) -> SemanticsTypeId; + // Converts an expression for use as a type. // TODO: This should eventually return a type ID. auto ExpressionAsType(ParseTree::Node parse_node, SemanticsNodeId value_id) @@ -282,12 +286,9 @@ class SemanticsContext { profile_type, llvm::function_ref make_node) -> SemanticsTypeId; - // Forms a canonical type ID for an already-existing node describing a type. - template - auto CanonicalizeTypeImpl(SemanticsNodeKind kind, SemanticsNodeId node_id, - ProfileType profile_type) -> SemanticsTypeId { - return CanonicalizeTypeImpl(kind, profile_type, [&] { return node_id; }); - } + // Forms a canonical type ID for a type. If the type is new, adds the node to + // the current block. + auto CanonicalizeTypeAndAddNodeIfNew(SemanticsNode node) -> SemanticsTypeId; auto current_scope() -> ScopeStackEntry& { return scope_stack_.back(); } diff --git a/toolchain/semantics/semantics_handle_operator.cpp b/toolchain/semantics/semantics_handle_operator.cpp index 0526db8a0418..bb7cfa385e17 100644 --- a/toolchain/semantics/semantics_handle_operator.cpp +++ b/toolchain/semantics/semantics_handle_operator.cpp @@ -93,14 +93,34 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context, // Figure out the operator for the token. auto token = context.parse_tree().node_token(parse_node); switch (auto token_kind = context.tokens().GetKind(token)) { - case TokenKind::Not: - value_id = context.ImplicitAsBool(parse_node, value_id); + case TokenKind::Amp: { + // Only durable reference expressions can have their address taken. + switch ( + GetSemanticsExpressionCategory(context.semantics_ir(), value_id)) { + case SemanticsExpressionCategory::DurableReference: + break; + case SemanticsExpressionCategory::EphemeralReference: + CARBON_DIAGNOSTIC(AddressOfEphemeralReference, Error, + "Cannot take the address of a temporary object."); + context.emitter().Emit(parse_node, AddressOfEphemeralReference); + break; + default: + CARBON_DIAGNOSTIC( + AddressOfNonReference, Error, + "Cannot take the address of non-reference expression."); + context.emitter().Emit(parse_node, AddressOfNonReference); + break; + } context.AddNodeAndPush( parse_node, - SemanticsNode::UnaryOperatorNot::Make( - parse_node, context.semantics_ir().GetNode(value_id).type_id(), + SemanticsNode::AddressOf::Make( + parse_node, + context.GetPointerType( + parse_node, + context.semantics_ir().GetNode(value_id).type_id()), value_id)); return true; + } case TokenKind::Const: { // `const (const T)` is probably not what the developer intended. @@ -121,6 +141,18 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context, return true; } + case TokenKind::Not: + value_id = context.ImplicitAsBool(parse_node, value_id); + context.AddNodeAndPush( + parse_node, + SemanticsNode::UnaryOperatorNot::Make( + parse_node, context.semantics_ir().GetNode(value_id).type_id(), + value_id)); + return true; + + case TokenKind::Star: + return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); + default: return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); } diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index 78b3bd1f9abe..cfe333f97c1e 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -217,6 +217,7 @@ static auto GetTypePrecedence(SemanticsNodeKind kind) -> int { // now, all cross-references refer to builtin types from the prelude. return 0; + case SemanticsNodeKind::AddressOf: case SemanticsNodeKind::Assign: case SemanticsNodeKind::BinaryOperatorAdd: case SemanticsNodeKind::BindName: @@ -285,7 +286,8 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { out << "const "; // Add parentheses if required. - auto inner_type_node_id = GetType(node.GetAsConstType()); + auto inner_type_node_id = + GetTypeAllowBuiltinTypes(node.GetAsConstType()); if (GetTypePrecedence(GetNode(inner_type_node_id).kind()) < GetTypePrecedence(node.kind())) { out << "("; @@ -301,7 +303,8 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::PointerType: { if (step.index == 0) { steps.push_back(step.Next()); - steps.push_back({.node_id = GetType(node.GetAsPointerType())}); + steps.push_back( + {.node_id = GetTypeAllowBuiltinTypes(node.GetAsPointerType())}); } else if (step.index == 1) { out << "*"; } @@ -354,6 +357,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { {.node_id = GetTypeAllowBuiltinTypes(refs[step.index])}); break; } + case SemanticsNodeKind::AddressOf: case SemanticsNodeKind::Assign: case SemanticsNodeKind::BinaryOperatorAdd: case SemanticsNodeKind::BindName: @@ -401,4 +405,86 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { return str; } +auto GetSemanticsExpressionCategory(const SemanticsIR& semantics_ir, + SemanticsNodeId node_id) + -> SemanticsExpressionCategory { + const SemanticsIR* ir = &semantics_ir; + while (true) { + auto node = ir->GetNode(node_id); + switch (node.kind()) { + case SemanticsNodeKind::Invalid: + case SemanticsNodeKind::Assign: + case SemanticsNodeKind::Branch: + case SemanticsNodeKind::BranchIf: + case SemanticsNodeKind::BranchWithArg: + case SemanticsNodeKind::FunctionDeclaration: + case SemanticsNodeKind::Namespace: + case SemanticsNodeKind::Return: + case SemanticsNodeKind::ReturnExpression: + case SemanticsNodeKind::StructTypeField: + return SemanticsExpressionCategory::NotExpression; + + case SemanticsNodeKind::CrossReference: { + auto [xref_id, xref_node_id] = node.GetAsCrossReference(); + ir = &semantics_ir.GetCrossReferenceIR(xref_id); + node_id = xref_node_id; + continue; + } + + case SemanticsNodeKind::Call: + // TODO: This should eventually be Initializing. + return SemanticsExpressionCategory::Value; + + case SemanticsNodeKind::BindName: { + auto [name_id, value_id] = node.GetAsBindName(); + node_id = value_id; + continue; + } + + case SemanticsNodeKind::AddressOf: + case SemanticsNodeKind::BinaryOperatorAdd: + case SemanticsNodeKind::BlockArg: + case SemanticsNodeKind::BoolLiteral: + case SemanticsNodeKind::Builtin: + case SemanticsNodeKind::ConstType: + case SemanticsNodeKind::IntegerLiteral: + case SemanticsNodeKind::PointerType: + case SemanticsNodeKind::RealLiteral: + case SemanticsNodeKind::StringLiteral: + case SemanticsNodeKind::StructType: + case SemanticsNodeKind::TupleType: + case SemanticsNodeKind::UnaryOperatorNot: + return SemanticsExpressionCategory::Value; + + case SemanticsNodeKind::StructMemberAccess: { + auto [base_id, member_index] = node.GetAsStructMemberAccess(); + node_id = base_id; + continue; + } + + case SemanticsNodeKind::Index: { + auto [base_id, index_id] = node.GetAsIndex(); + node_id = base_id; + continue; + } + + case SemanticsNodeKind::StubReference: { + node_id = node.GetAsStubReference(); + continue; + } + + case SemanticsNodeKind::StructValue: + case SemanticsNodeKind::TupleValue: + // TODO: Eventually these will depend on the context in which the value + // is used, and could be either Value or Initializing. We may want + // different node kinds for a struct/tuple initializer versus a + // struct/tuple value construction. + return SemanticsExpressionCategory::Value; + + case SemanticsNodeKind::VarStorage: + return SemanticsExpressionCategory::DurableReference; + } + } +} + } // namespace Carbon diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h index 928c3e729d31..04efae736a31 100644 --- a/toolchain/semantics/semantics_ir.h +++ b/toolchain/semantics/semantics_ir.h @@ -82,6 +82,12 @@ class SemanticsIR { } auto Print(llvm::raw_ostream& out, bool include_builtins) const -> void; + // Returns the requested IR. + auto GetCrossReferenceIR(SemanticsCrossReferenceIRId xref_id) const + -> const SemanticsIR& { + return *cross_reference_irs_[xref_id.index]; + } + // Adds a callable, returning an ID to reference it. auto AddFunction(SemanticsFunction function) -> SemanticsFunctionId { SemanticsFunctionId id(functions_.size()); @@ -332,6 +338,30 @@ class SemanticsIR { SemanticsNodeBlockId top_node_block_id_ = SemanticsNodeBlockId::Invalid; }; +// The expression category of a semantics node. See /docs/design/values.md for +// details. +enum class SemanticsExpressionCategory { + // This node does not correspond to an expression, and as such has no + // category. + NotExpression, + // This node represents a value expression. + Value, + // This node represents a durable reference expression, that denotes an + // object that outlives the current full expression context. + DurableReference, + // This node represents an ephemeral reference expression, that denotes an + // object that does not outlive the current full expression context. + EphemeralReference, + // This node represents an initializing expression, that describes how to + // initialize an object. + Initializing, +}; + +// Returns the expression category for a node. +auto GetSemanticsExpressionCategory(const SemanticsIR& semantics_ir, + SemanticsNodeId node_id) + -> SemanticsExpressionCategory; + } // namespace Carbon #endif // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_ diff --git a/toolchain/semantics/semantics_node.cpp b/toolchain/semantics/semantics_node.cpp index 189e40d01c82..75e24afc66a6 100644 --- a/toolchain/semantics/semantics_node.cpp +++ b/toolchain/semantics/semantics_node.cpp @@ -20,7 +20,7 @@ static auto PrintArgs(llvm::raw_ostream& out, std::pair args) -> void { out << ", arg1: " << args.second; } -void SemanticsNode::Print(llvm::raw_ostream& out) const { +auto SemanticsNode::Print(llvm::raw_ostream& out) const -> void { out << "{kind: " << kind_; switch (kind_) { #define CARBON_SEMANTICS_NODE_KIND(Name) \ diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 1440af4e7993..98d6bcf33108 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -307,6 +307,9 @@ class SemanticsNode { } }; + using AddressOf = SemanticsNode::Factory; + using Assign = SemanticsNode::FactoryNoType; diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index 5db11dc39a21..5f1387bd5251 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -27,6 +27,7 @@ CARBON_SEMANTICS_NODE_KIND(Invalid) // A cross-reference between IRs. CARBON_SEMANTICS_NODE_KIND(CrossReference) +CARBON_SEMANTICS_NODE_KIND(AddressOf) CARBON_SEMANTICS_NODE_KIND(Assign) CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd) CARBON_SEMANTICS_NODE_KIND(BindName) diff --git a/toolchain/semantics/testdata/pointer/address_of_lvalue.carbon b/toolchain/semantics/testdata/pointer/address_of_lvalue.carbon new file mode 100644 index 000000000000..29c555780be6 --- /dev/null +++ b/toolchain/semantics/testdata/pointer/address_of_lvalue.carbon @@ -0,0 +1,241 @@ +// 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 + +fn F(param: i32) { + var s: {.a: i32, .b: i32} = {.a = 1, .b = 2}; + + var p: {.a: i32, .b: i32}* = &s; + var q: i32* = &s.a; + var r: i32* = &s.b; + + var t: (i32, i32) = (1, 2); + var t0: i32* = &t[0]; + var t1: i32* = &t[1]; + + // TODO: Once we start treating parameters as `let` bindings by default, we + // should reject this, but `fn F(var n: i32)` should still work. + var param_addr: i32* = ¶m; +} + +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block2, body: {block4}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 0, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: param, +// CHECK:STDOUT: s, +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: p, +// CHECK:STDOUT: q, +// CHECK:STDOUT: r, +// CHECK:STDOUT: t, +// CHECK:STDOUT: t0, +// CHECK:STDOUT: t1, +// CHECK:STDOUT: param_addr, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: node+37, +// CHECK:STDOUT: node+39, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: typeTypeType, +// CHECK:STDOUT: typeTypeType, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: type0, +// CHECK:STDOUT: type0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+0, type: type0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str3, arg1: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str4, arg1: type0}, +// CHECK:STDOUT: {kind: StructType, arg0: block5, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+6, type: type1}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str3, arg1: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+8, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str4, arg1: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type0}, +// CHECK:STDOUT: {kind: StructValue, arg0: block7, type: type1}, +// CHECK:STDOUT: {kind: Assign, arg0: node+6, arg1: node+14}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str3, arg1: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str4, arg1: type0}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type2}, +// CHECK:STDOUT: {kind: BindName, arg0: str5, arg1: node+19, type: type2}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+6, type: type2}, +// CHECK:STDOUT: {kind: Assign, arg0: node+19, arg1: node+21}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str6, arg1: node+24, type: type3}, +// CHECK:STDOUT: {kind: StructMemberAccess, arg0: node+6, arg1: member0, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+26, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+24, arg1: node+27}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str7, arg1: node+30, type: type3}, +// CHECK:STDOUT: {kind: StructMemberAccess, arg0: node+6, arg1: member1, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+32, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+30, arg1: node+33}, +// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType}, +// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block9, type: type4}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type5}, +// CHECK:STDOUT: {kind: BindName, arg0: str8, arg1: node+40, type: type5}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+42, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+44, type: type0}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block10, type: type5}, +// CHECK:STDOUT: {kind: Assign, arg0: node+40, arg1: node+46}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str9, arg1: node+49, type: type3}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int4, type: type0}, +// CHECK:STDOUT: {kind: Index, arg0: node+40, arg1: node+51, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+52, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+49, arg1: node+53}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str10, arg1: node+56, type: type3}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int5, type: type0}, +// CHECK:STDOUT: {kind: Index, arg0: node+40, arg1: node+58, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+59, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+56, arg1: node+60}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str11, arg1: node+63, type: type3}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+0, type: type3}, +// CHECK:STDOUT: {kind: Assign, arg0: node+63, arg1: node+65}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: node+22, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: node+24, +// CHECK:STDOUT: node+25, +// CHECK:STDOUT: node+26, +// CHECK:STDOUT: node+27, +// CHECK:STDOUT: node+28, +// CHECK:STDOUT: node+29, +// CHECK:STDOUT: node+30, +// CHECK:STDOUT: node+31, +// CHECK:STDOUT: node+32, +// CHECK:STDOUT: node+33, +// CHECK:STDOUT: node+34, +// CHECK:STDOUT: node+35, +// CHECK:STDOUT: node+36, +// CHECK:STDOUT: node+37, +// CHECK:STDOUT: node+38, +// CHECK:STDOUT: node+39, +// CHECK:STDOUT: node+40, +// CHECK:STDOUT: node+41, +// CHECK:STDOUT: node+42, +// CHECK:STDOUT: node+43, +// CHECK:STDOUT: node+44, +// CHECK:STDOUT: node+45, +// CHECK:STDOUT: node+46, +// CHECK:STDOUT: node+47, +// CHECK:STDOUT: node+48, +// CHECK:STDOUT: node+49, +// CHECK:STDOUT: node+50, +// CHECK:STDOUT: node+51, +// CHECK:STDOUT: node+52, +// CHECK:STDOUT: node+53, +// CHECK:STDOUT: node+54, +// CHECK:STDOUT: node+55, +// CHECK:STDOUT: node+56, +// CHECK:STDOUT: node+57, +// CHECK:STDOUT: node+58, +// CHECK:STDOUT: node+59, +// CHECK:STDOUT: node+60, +// CHECK:STDOUT: node+61, +// CHECK:STDOUT: node+62, +// CHECK:STDOUT: node+63, +// CHECK:STDOUT: node+64, +// CHECK:STDOUT: node+65, +// CHECK:STDOUT: node+66, +// CHECK:STDOUT: node+67, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+35, +// CHECK:STDOUT: node+36, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+43, +// CHECK:STDOUT: node+45, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] diff --git a/toolchain/semantics/testdata/pointer/basic.carbon b/toolchain/semantics/testdata/pointer/basic.carbon index 9144acf78724..837c18503103 100644 --- a/toolchain/semantics/testdata/pointer/basic.carbon +++ b/toolchain/semantics/testdata/pointer/basic.carbon @@ -6,11 +6,9 @@ fn F() -> i32 { var n: i32 = 0; - var p: i32*; + var p: i32* = &n; - // TODO: Test taking address and dereference. - // p = &n; - // *p = 1; + // TODO: Dereference. // return *p; return 0; @@ -46,8 +44,10 @@ fn F() -> i32 { // CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, // CHECK:STDOUT: {kind: VarStorage, type: type1}, // CHECK:STDOUT: {kind: BindName, arg0: str2, arg1: node+6, type: type1}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+1, type: type1}, +// CHECK:STDOUT: {kind: Assign, arg0: node+6, arg1: node+8}, // CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, -// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+8}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+10}, // CHECK:STDOUT: ] // CHECK:STDOUT: node_blocks: [ // CHECK:STDOUT: [ @@ -65,5 +65,7 @@ fn F() -> i32 { // CHECK:STDOUT: node+7, // CHECK:STDOUT: node+8, // CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, // CHECK:STDOUT: ], // CHECK:STDOUT: ] diff --git a/toolchain/semantics/testdata/pointer/fail_address_of_value.carbon b/toolchain/semantics/testdata/pointer/fail_address_of_value.carbon new file mode 100644 index 000000000000..1520b2c5fc56 --- /dev/null +++ b/toolchain/semantics/testdata/pointer/fail_address_of_value.carbon @@ -0,0 +1,298 @@ +// 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 + +fn G() -> i32; + +fn H() -> {.a: i32}; + +fn AddressOfLiteral() { + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &0; + // CHECK:STDERR: ^ + &0; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &true; + // CHECK:STDERR: ^ + &true; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &1.0; + // CHECK:STDERR: ^ + &1.0; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &"Hello"; + // CHECK:STDERR: ^ + &"Hello"; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &(1, 2); + // CHECK:STDERR: ^ + &(1, 2); + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &{.a = 5}; + // CHECK:STDERR: ^ + &{.a = 5}; +} + +fn AddressOfOperator() { + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &(1 + 1); + // CHECK:STDERR: ^ + &(1 + 1); + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &H().a; + // CHECK:STDERR: ^ + &H().a; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &(not true); + // CHECK:STDERR: ^ + &(not true); +} + +fn AddressOfCall() { + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &G(); + // CHECK:STDERR: ^ + &G(); +} + +fn AddressOfType() { + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &i32; + // CHECK:STDERR: ^ + &i32; + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &(const i32*); + // CHECK:STDERR: ^ + &(const i32*); +} + +fn AddressOfTupleElementValue() { + // CHECK:STDERR: fail_address_of_value.carbon:[[@LINE+3]]:3: Cannot take the address of non-reference expression. + // CHECK:STDERR: &((1, 2)[0]); + // CHECK:STDERR: ^ + &((1, 2)[0]); +} + +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block0, return_type: type0}, +// CHECK:STDOUT: {name: str1, param_refs: block0, return_type: type1}, +// CHECK:STDOUT: {name: str3, param_refs: block0, body: {block3}}}, +// CHECK:STDOUT: {name: str5, param_refs: block0, body: {block7}}}, +// CHECK:STDOUT: {name: str6, param_refs: block0, body: {block8}}}, +// CHECK:STDOUT: {name: str7, param_refs: block0, body: {block9}}}, +// CHECK:STDOUT: {name: str8, param_refs: block0, body: {block10}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 0, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 5, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 0, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: {mantissa: 10, exponent: -1, is_decimal: 1}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: G, +// CHECK:STDOUT: H, +// CHECK:STDOUT: a, +// CHECK:STDOUT: AddressOfLiteral, +// CHECK:STDOUT: Hello, +// CHECK:STDOUT: AddressOfOperator, +// CHECK:STDOUT: AddressOfCall, +// CHECK:STDOUT: AddressOfType, +// CHECK:STDOUT: AddressOfTupleElementValue, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: nodeBoolType, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: nodeFloatingPointType, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: nodeStringType, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: node+29, +// CHECK:STDOUT: node+49, +// CHECK:STDOUT: node+51, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: type0, +// CHECK:STDOUT: type0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str2, arg1: type0}, +// CHECK:STDOUT: {kind: StructType, arg0: block2, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function2}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+5, type: type2}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type3}, +// CHECK:STDOUT: {kind: PointerType, arg0: type3, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+8, type: type4}, +// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type5}, +// CHECK:STDOUT: {kind: PointerType, arg0: type5, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+11, type: type6}, +// CHECK:STDOUT: {kind: StringLiteral, arg0: str4, type: type7}, +// CHECK:STDOUT: {kind: PointerType, arg0: type7, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+14, type: type8}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+17, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+19, type: type0}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block4, type: type9}, +// CHECK:STDOUT: {kind: PointerType, arg0: type9, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+22, type: type10}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str2, arg1: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+25, type: type0}, +// CHECK:STDOUT: {kind: StructValue, arg0: block6, type: type1}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+28, type: type11}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function3}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int4, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int5, type: type0}, +// CHECK:STDOUT: {kind: BinaryOperatorAdd, arg0: node+33, arg1: node+34, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+35, type: type2}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function1, type: type1}, +// CHECK:STDOUT: {kind: StructMemberAccess, arg0: node+37, arg1: member0, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+38, type: type2}, +// CHECK:STDOUT: {kind: BoolLiteral, arg0: true, type: type3}, +// CHECK:STDOUT: {kind: UnaryOperatorNot, arg0: node+40, type: type3}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+41, type: type4}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function4}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+45, type: type2}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function5}, +// CHECK:STDOUT: {kind: PointerType, arg0: typeTypeType, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: nodeIntegerType, type: type12}, +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type13, type: typeTypeType}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+52, type: type12}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function6}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int6, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+56, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int7, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+58, type: type0}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block11, type: type9}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int8, type: type0}, +// CHECK:STDOUT: {kind: Index, arg0: node+60, arg1: node+61, type: type0}, +// CHECK:STDOUT: {kind: AddressOf, arg0: node+62, type: type2}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+32, +// CHECK:STDOUT: node+44, +// CHECK:STDOUT: node+48, +// CHECK:STDOUT: node+55, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: node+14, +// CHECK:STDOUT: node+15, +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+17, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+19, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: node+22, +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: node+24, +// CHECK:STDOUT: node+25, +// CHECK:STDOUT: node+27, +// CHECK:STDOUT: node+28, +// CHECK:STDOUT: node+29, +// CHECK:STDOUT: node+30, +// CHECK:STDOUT: node+31, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+26, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+27, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+33, +// CHECK:STDOUT: node+34, +// CHECK:STDOUT: node+35, +// CHECK:STDOUT: node+36, +// CHECK:STDOUT: node+37, +// CHECK:STDOUT: node+38, +// CHECK:STDOUT: node+39, +// CHECK:STDOUT: node+40, +// CHECK:STDOUT: node+41, +// CHECK:STDOUT: node+42, +// CHECK:STDOUT: node+43, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+45, +// CHECK:STDOUT: node+46, +// CHECK:STDOUT: node+47, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+49, +// CHECK:STDOUT: node+50, +// CHECK:STDOUT: node+51, +// CHECK:STDOUT: node+52, +// CHECK:STDOUT: node+53, +// CHECK:STDOUT: node+54, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+56, +// CHECK:STDOUT: node+57, +// CHECK:STDOUT: node+58, +// CHECK:STDOUT: node+59, +// CHECK:STDOUT: node+60, +// CHECK:STDOUT: node+61, +// CHECK:STDOUT: node+62, +// CHECK:STDOUT: node+63, +// CHECK:STDOUT: node+64, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+57, +// CHECK:STDOUT: node+59, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ]