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: ]