diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 58da67203898..69a642d21c61 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -103,6 +103,7 @@ CARBON_DIAGNOSTIC_KIND(NoMatchingCall) CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch) CARBON_DIAGNOSTIC_KIND(CallArgTypeMismatch) CARBON_DIAGNOSTIC_KIND(MissingReturnStatement) +CARBON_DIAGNOSTIC_KIND(RepeatedConst) CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpression) CARBON_DIAGNOSTIC_KIND(ReturnStatementImplicitNote) CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpression) diff --git a/toolchain/lowering/lowering_handle_type.cpp b/toolchain/lowering/lowering_handle_type.cpp index 6c8711b200cc..c83947938bbf 100644 --- a/toolchain/lowering/lowering_handle_type.cpp +++ b/toolchain/lowering/lowering_handle_type.cpp @@ -6,6 +6,18 @@ namespace Carbon { +auto LoweringHandleConstType(LoweringFunctionContext& context, + SemanticsNodeId node_id, SemanticsNode /*node*/) + -> void { + context.SetLocal(node_id, context.GetTypeAsValue()); +} + +auto LoweringHandlePointerType(LoweringFunctionContext& context, + SemanticsNodeId node_id, SemanticsNode /*node*/) + -> void { + context.SetLocal(node_id, context.GetTypeAsValue()); +} + auto LoweringHandleStructType(LoweringFunctionContext& context, SemanticsNodeId node_id, SemanticsNode /*node*/) -> void { diff --git a/toolchain/semantics/BUILD b/toolchain/semantics/BUILD index 247eb4d00152..af352d82ec86 100644 --- a/toolchain/semantics/BUILD +++ b/toolchain/semantics/BUILD @@ -21,7 +21,10 @@ cc_library( srcs = ["semantics_node_kind.cpp"], hdrs = ["semantics_node_kind.h"], textual_hdrs = ["semantics_node_kind.def"], - deps = ["//common:enum_base"], + deps = [ + "//common:enum_base", + "@llvm-project//llvm:Support", + ], ) cc_library( diff --git a/toolchain/semantics/semantics_context.cpp b/toolchain/semantics/semantics_context.cpp index 17627166ab0a..884431412c40 100644 --- a/toolchain/semantics/semantics_context.cpp +++ b/toolchain/semantics/semantics_context.cpp @@ -449,6 +449,40 @@ auto SemanticsContext::ParamOrArgSave(bool for_args) -> void { params_or_args.push_back(entry_node_id); } +auto SemanticsContext::CanonicalizeTypeImpl( + SemanticsNodeKind kind, + llvm::function_ref profile_type, + llvm::function_ref make_node) -> SemanticsTypeId { + llvm::FoldingSetNodeID canonical_id; + kind.Profile(canonical_id); + profile_type(canonical_id); + + void* insert_pos; + auto* node = + canonical_type_nodes_.FindNodeOrInsertPos(canonical_id, insert_pos); + if (node != nullptr) { + return node->type_id(); + } + + auto node_id = make_node(); + auto type_id = semantics_ir_->AddType(node_id); + CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second); + type_node_storage_.push_back( + std::make_unique(canonical_id, type_id)); + + // In a debug build, check that our insertion position is still valid. It + // could have been invalidated by a misbehaving `make_node`. + CARBON_DCHECK([&] { + void* check_insert_pos; + auto* check_node = canonical_type_nodes_.FindNodeOrInsertPos( + canonical_id, check_insert_pos); + return !check_node && insert_pos == check_insert_pos; + }()) << "Type was created recursively during canonicalization"; + + canonical_type_nodes_.InsertNode(type_node_storage_.back().get(), insert_pos); + return type_id; +} + auto SemanticsContext::CanonicalizeType(SemanticsNodeId node_id) -> SemanticsTypeId { auto node = semantics_ir_->GetNode(node_id); @@ -464,70 +498,84 @@ auto SemanticsContext::CanonicalizeType(SemanticsNodeId node_id) return it->second; } - auto type_id = semantics_ir_->AddType(node_id); - CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second); - return type_id; + 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 SemanticsContext::CanonicalizeStructType(ParseTree::Node parse_node, SemanticsNodeBlockId refs_id) -> SemanticsTypeId { - // Construct the field structure for lookup. - auto refs = semantics_ir_->GetNodeBlock(refs_id); - llvm::FoldingSetNodeID canonical_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); - } - - // If a struct with matching fields was already created, reuse it. - void* insert_pos; - auto* node = - canonical_struct_types_.FindNodeOrInsertPos(canonical_id, insert_pos); - if (node != nullptr) { - return node->type_id(); - } - - // The struct doesn't already exist, so create and store it as canonical. - auto node_id = AddNode(SemanticsNode::StructType::Make( - parse_node, SemanticsTypeId::TypeType, refs_id)); - auto type_id = semantics_ir_->AddType(node_id); - CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second); - canonical_types_nodes_.push_back( - std::make_unique(canonical_id, type_id)); - canonical_struct_types_.InsertNode(canonical_types_nodes_.back().get(), - insert_pos); - return type_id; + 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); } auto SemanticsContext::CanonicalizeTupleType( ParseTree::Node parse_node, llvm::SmallVector&& type_ids) -> SemanticsTypeId { - llvm::FoldingSetNodeID canonical_id; - for (const auto& type_id : type_ids) { - canonical_id.AddInteger(type_id.index); - } - // If a tuple with matching fields was already created, reuse it. - void* insert_pos; - auto* node = - canonical_tuple_types_.FindNodeOrInsertPos(canonical_id, insert_pos); - if (node != nullptr) { - return node->type_id(); - } - // The tuple type doesn't already exist, so create and store it as canonical. - auto type_block_id = semantics_ir_->AddTypeBlock(); - auto& type_block = semantics_ir_->GetTypeBlock(type_block_id); - type_block = std::move(type_ids); - auto node_id = AddNode(SemanticsNode::TupleType::Make( - parse_node, SemanticsTypeId::TypeType, type_block_id)); - auto type_id = semantics_ir_->AddType(node_id); - CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second); - canonical_types_nodes_.push_back( - std::make_unique(canonical_id, type_id)); - canonical_tuple_types_.InsertNode(canonical_types_nodes_.back().get(), - insert_pos); + auto profile_tuple = [&](llvm::FoldingSetNodeID& canonical_id) { + for (const auto& type_id : type_ids) { + canonical_id.AddInteger(type_id.index); + } + }; + auto make_tuple_node = [&] { + auto type_block_id = semantics_ir_->AddTypeBlock(); + auto& type_block = semantics_ir_->GetTypeBlock(type_block_id); + type_block = std::move(type_ids); + return AddNode(SemanticsNode::TupleType::Make( + parse_node, SemanticsTypeId::TypeType, type_block_id)); + }; + return CanonicalizeTypeImpl(SemanticsNodeKind::TupleType, profile_tuple, + make_tuple_node); +} + +auto SemanticsContext::GetUnqualifiedType(SemanticsTypeId type_id) + -> SemanticsTypeId { + SemanticsNode type_node = + semantics_ir_->GetNode(semantics_ir_->GetType(type_id)); + if (type_node.kind() == SemanticsNodeKind::ConstType) + return type_node.GetAsConstType(); return type_id; } diff --git a/toolchain/semantics/semantics_context.h b/toolchain/semantics/semantics_context.h index 52f633d438c4..6e80050cf629 100644 --- a/toolchain/semantics/semantics_context.h +++ b/toolchain/semantics/semantics_context.h @@ -169,6 +169,9 @@ class SemanticsContext { ImplicitAsRequired(parse_node, value_id, SemanticsTypeId::TypeType)); } + // Removes any top-level `const` qualifiers from a type. + auto GetUnqualifiedType(SemanticsTypeId type_id) -> SemanticsTypeId; + // Starts handling parameters or arguments. auto ParamOrArgStart() -> void; @@ -229,7 +232,7 @@ class SemanticsContext { Compatible, }; - // A FoldingSet node for a struct or tuple type. + // A FoldingSet node for a type. class TypeNode : public llvm::FastFoldingSetNode { public: explicit TypeNode(const llvm::FoldingSetNodeID& node_id, @@ -262,6 +265,30 @@ class SemanticsContext { auto ImplicitAsImpl(SemanticsNodeId value_id, SemanticsTypeId as_type_id, SemanticsNodeId* output_value_id) -> ImplicitAsKind; + // Forms a canonical type ID for a type. This function is given two + // callbacks: + // + // `profile_type(canonical_id)` is called to build a fingerprint for this + // type. The ID should be distinct for all distinct type values with the same + // `kind`. + // + // `make_node()` is called to obtain a `SemanticsNodeId` that describes the + // type. It is only called if the type does not already exist, so can be used + // to lazily build the `SemanticsNode`. `make_node()` is not permitted to + // directly or indirectly canonicalize any types. + auto CanonicalizeTypeImpl( + SemanticsNodeKind kind, + llvm::function_ref + 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; }); + } + auto current_scope() -> ScopeStackEntry& { return scope_stack_.back(); } // Tokens for getting data on literals. @@ -316,22 +343,16 @@ class SemanticsContext { llvm::DenseMap> name_lookup_; - // Tracks types which have been used, so that they aren't repeatedly added to - // SemanticsIR. + // Cache of the mapping from nodes to types, to avoid recomputing the folding + // set ID. llvm::DenseMap canonical_types_; - // Tracks struct type literals which have been defined, so that they aren't - // repeatedly redefined. - llvm::FoldingSet canonical_struct_types_; + // Tracks the canonical representation of types that have been defined. + llvm::FoldingSet canonical_type_nodes_; - // Tracks tuple type literals which have been defined, so that they aren't - // repeatedly redefined. - llvm::FoldingSet canonical_tuple_types_; - - // Storage for the nodes in canonical_struct_types_ and - // canonical_tuple_types_. This stores in pointers so that FoldingSet can have - // stable pointers. - llvm::SmallVector> canonical_types_nodes_; + // Storage for the nodes in canonical_type_nodes_. This stores in pointers so + // that FoldingSet can have stable pointers. + llvm::SmallVector> type_node_storage_; }; // Parse node handlers. Returns false for unrecoverable errors. diff --git a/toolchain/semantics/semantics_handle_operator.cpp b/toolchain/semantics/semantics_handle_operator.cpp index 5437c3d7d72a..7ee5139037fd 100644 --- a/toolchain/semantics/semantics_handle_operator.cpp +++ b/toolchain/semantics/semantics_handle_operator.cpp @@ -25,7 +25,7 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context, SemanticsNode::BinaryOperatorAdd::Make( parse_node, context.semantics_ir().GetNode(lhs_id).type_id(), lhs_id, rhs_id)); - break; + return true; case TokenKind::And: case TokenKind::Or: { @@ -49,19 +49,33 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context, SemanticsNode::BlockArg::Make( parse_node, context.semantics_ir().GetNode(rhs_id).type_id(), resume_block_id)); - break; + return true; } default: return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); } - - return true; } auto SemanticsHandlePostfixOperator(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandlePostfixOperator"); + auto value_id = context.node_stack().PopExpression(); + + // 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::Star: { + auto inner_type_id = context.ExpressionAsType(parse_node, value_id); + context.AddNodeAndPush( + parse_node, + SemanticsNode::PointerType::Make( + parse_node, SemanticsTypeId::TypeType, inner_type_id)); + return true; + } + + default: + CARBON_FATAL() << "Unexpected postfix operator " << token_kind; + } } auto SemanticsHandlePrefixOperator(SemanticsContext& context, @@ -78,13 +92,30 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context, SemanticsNode::UnaryOperatorNot::Make( parse_node, context.semantics_ir().GetNode(value_id).type_id(), value_id)); - break; + return true; + + case TokenKind::Const: { + // `const (const T)` is probably not what the developer intended. + // TODO: Detect `const (const T)*` and suggest moving the `*` inside the + // parentheses. + if (context.semantics_ir().GetNode(value_id).kind() == + SemanticsNodeKind::ConstType) { + CARBON_DIAGNOSTIC(RepeatedConst, Warning, + "`const` applied repeatedly to the same type has no " + "additional effect."); + context.emitter().Emit(parse_node, RepeatedConst); + } + auto inner_type_id = context.ExpressionAsType(parse_node, value_id); + context.AddNodeAndPush( + parse_node, + SemanticsNode::ConstType::Make(parse_node, SemanticsTypeId::TypeType, + inner_type_id)); + return true; + } default: return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind)); } - - return true; } auto SemanticsHandleShortCircuitOperand(SemanticsContext& context, @@ -115,7 +146,7 @@ auto SemanticsHandleShortCircuitOperand(SemanticsContext& context, break; default: - CARBON_FATAL() << "Unexpected short-circuiting operator " << parse_node; + CARBON_FATAL() << "Unexpected short-circuiting operator " << token_kind; } // Create a block for the right-hand side and for the continuation. diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp index dd8d861ea4c8..7a649f51865e 100644 --- a/toolchain/semantics/semantics_ir.cpp +++ b/toolchain/semantics/semantics_ir.cpp @@ -197,6 +197,54 @@ auto SemanticsIR::Print(llvm::raw_ostream& out, bool include_builtins) const PrintBlock(out, "node_blocks", node_blocks_); } +// Map a node kind representing a type into an integer describing the +// precedence of that type's syntax. Higher numbers correspond to higher +// precedence. +static auto GetTypePrecedence(SemanticsNodeKind kind) -> int { + switch (kind) { + case SemanticsNodeKind::Builtin: + case SemanticsNodeKind::StructType: + case SemanticsNodeKind::TupleType: + return 0; + case SemanticsNodeKind::ConstType: + return -1; + case SemanticsNodeKind::PointerType: + return -2; + + case SemanticsNodeKind::CrossReference: + // TODO: Once we support stringification of cross-references, we'll need + // to determine the precedence of the target of the cross-reference. For + // now, all cross-references refer to builtin types from the prelude. + return 0; + + case SemanticsNodeKind::Assign: + case SemanticsNodeKind::BinaryOperatorAdd: + case SemanticsNodeKind::BindName: + case SemanticsNodeKind::BlockArg: + case SemanticsNodeKind::BoolLiteral: + case SemanticsNodeKind::Branch: + case SemanticsNodeKind::BranchIf: + case SemanticsNodeKind::BranchWithArg: + case SemanticsNodeKind::Call: + case SemanticsNodeKind::FunctionDeclaration: + case SemanticsNodeKind::IntegerLiteral: + case SemanticsNodeKind::Invalid: + case SemanticsNodeKind::Namespace: + case SemanticsNodeKind::RealLiteral: + case SemanticsNodeKind::Return: + case SemanticsNodeKind::ReturnExpression: + case SemanticsNodeKind::StringLiteral: + case SemanticsNodeKind::StructMemberAccess: + case SemanticsNodeKind::StructTypeField: + case SemanticsNodeKind::StructValue: + case SemanticsNodeKind::StubReference: + case SemanticsNodeKind::TupleValue: + case SemanticsNodeKind::UnaryOperatorNot: + case SemanticsNodeKind::VarStorage: + CARBON_FATAL() << "GetTypePrecedence for non-type node kind " << kind; + } +} + auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { std::string str; llvm::raw_string_ostream out(str); @@ -206,9 +254,13 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { SemanticsNodeId node_id; // The index into node_id to print. Not used by all types. int index = 0; + + auto Next() const -> Step { + return {.node_id = node_id, .index = index + 1}; + } }; - llvm::SmallVector steps = { - {.node_id = GetTypeAllowBuiltinTypes(type_id)}}; + auto outer_node_id = GetTypeAllowBuiltinTypes(type_id); + llvm::SmallVector steps = {{.node_id = outer_node_id}}; while (!steps.empty()) { auto step = steps.pop_back_val(); @@ -227,10 +279,37 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { auto node = GetNode(step.node_id); switch (node.kind()) { + case SemanticsNodeKind::ConstType: { + if (step.index == 0) { + out << "const "; + + // Add parentheses if required. + auto inner_type_node_id = GetType(node.GetAsConstType()); + if (GetTypePrecedence(GetNode(inner_type_node_id).kind()) < + GetTypePrecedence(node.kind())) { + out << "("; + steps.push_back(step.Next()); + } + + steps.push_back({.node_id = inner_type_node_id}); + } else if (step.index == 1) { + out << ")"; + } + break; + } + case SemanticsNodeKind::PointerType: { + if (step.index == 0) { + steps.push_back(step.Next()); + steps.push_back({.node_id = GetType(node.GetAsPointerType())}); + } else if (step.index == 1) { + out << "*"; + } + break; + } case SemanticsNodeKind::StructType: { auto refs = GetNodeBlock(node.GetAsStructType()); if (refs.empty()) { - out << "{} as Type"; + out << "{}"; break; } else if (step.index == 0) { out << "{"; @@ -241,7 +320,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { break; } - steps.push_back({.node_id = step.node_id, .index = step.index + 1}); + steps.push_back(step.Next()); steps.push_back({.node_id = refs[step.index]}); break; } @@ -254,7 +333,7 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { case SemanticsNodeKind::TupleType: { auto refs = GetTypeBlock(node.GetAsTupleType()); if (refs.empty()) { - out << "() as type"; + out << "()"; break; } else if (step.index == 0) { out << "("; @@ -266,10 +345,10 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { if (step.index == 1) { out << ","; } - out << ") as type"; + out << ")"; break; } - steps.push_back({.node_id = step.node_id, .index = step.index + 1}); + steps.push_back(step.Next()); steps.push_back( {.node_id = GetTypeAllowBuiltinTypes(refs[step.index])}); break; @@ -308,6 +387,15 @@ auto SemanticsIR::StringifyType(SemanticsTypeId type_id) -> std::string { } } + // For `{}` or any tuple type, we've printed a non-type expression, so add a + // conversion to type `type`. + auto outer_node = GetNode(outer_node_id); + if (outer_node.kind() == SemanticsNodeKind::TupleType || + (outer_node.kind() == SemanticsNodeKind::StructType && + GetNodeBlock(outer_node.GetAsStructType()).empty())) { + out << " as type"; + } + return str; } diff --git a/toolchain/semantics/semantics_node.h b/toolchain/semantics/semantics_node.h index 7cb1752e06d5..a727fa59ab9b 100644 --- a/toolchain/semantics/semantics_node.h +++ b/toolchain/semantics/semantics_node.h @@ -357,6 +357,9 @@ class SemanticsNode { Factory; + using ConstType = + Factory; + class CrossReference : public FactoryBase; + using PointerType = + Factory; + using RealLiteral = Factory; diff --git a/toolchain/semantics/semantics_node_kind.def b/toolchain/semantics/semantics_node_kind.def index a5cd5748a482..55da830736a0 100644 --- a/toolchain/semantics/semantics_node_kind.def +++ b/toolchain/semantics/semantics_node_kind.def @@ -37,9 +37,11 @@ CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(BranchIf, TerminatorSequence) CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(BranchWithArg, Terminator) CARBON_SEMANTICS_NODE_KIND(Builtin) CARBON_SEMANTICS_NODE_KIND(Call) +CARBON_SEMANTICS_NODE_KIND(ConstType) CARBON_SEMANTICS_NODE_KIND(FunctionDeclaration) CARBON_SEMANTICS_NODE_KIND(IntegerLiteral) CARBON_SEMANTICS_NODE_KIND(Namespace) +CARBON_SEMANTICS_NODE_KIND(PointerType) CARBON_SEMANTICS_NODE_KIND(RealLiteral) CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(Return, Terminator) CARBON_SEMANTICS_NODE_KIND_WITH_TERMINATOR_KIND(ReturnExpression, Terminator) diff --git a/toolchain/semantics/semantics_node_kind.h b/toolchain/semantics/semantics_node_kind.h index 421c08e19f58..7ae40df2fc1c 100644 --- a/toolchain/semantics/semantics_node_kind.h +++ b/toolchain/semantics/semantics_node_kind.h @@ -8,6 +8,7 @@ #include #include "common/enum_base.h" +#include "llvm/ADT/FoldingSet.h" namespace Carbon { @@ -42,6 +43,10 @@ class SemanticsNodeKind : public CARBON_ENUM_BASE(SemanticsNodeKind) { // code block appears after all other instructions, and ends with a // terminator instruction. [[nodiscard]] auto terminator_kind() const -> SemanticsTerminatorKind; + + // Compute a fingerprint for this node kind, allowing its use as part of the + // key in a `FoldingSet`. + void Profile(llvm::FoldingSetNodeID& id) { id.AddInteger(AsInt()); } }; #define CARBON_SEMANTICS_NODE_KIND(Name) \ diff --git a/toolchain/semantics/semantics_node_stack.h b/toolchain/semantics/semantics_node_stack.h index 95561eab4d62..469e3750c13a 100644 --- a/toolchain/semantics/semantics_node_stack.h +++ b/toolchain/semantics/semantics_node_stack.h @@ -250,6 +250,7 @@ class SemanticsNodeStack { case ParseNodeKind::NameExpression: case ParseNodeKind::ParenExpression: case ParseNodeKind::PatternBinding: + case ParseNodeKind::PostfixOperator: case ParseNodeKind::PrefixOperator: case ParseNodeKind::ShortCircuitOperand: case ParseNodeKind::StructFieldValue: diff --git a/toolchain/semantics/testdata/const/collapse.carbon b/toolchain/semantics/testdata/const/collapse.carbon new file mode 100644 index 000000000000..b81d88385230 --- /dev/null +++ b/toolchain/semantics/testdata/const/collapse.carbon @@ -0,0 +1,70 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type3, body: {block4}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: p, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type2, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+3, type: type3}, +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: ConstType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type2, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+3}, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+4, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +// OK, `const (const i32)` is the same type as `const i32`. +// CHECK:STDERR: collapse.carbon:[[@LINE+3]]:25: `const` applied repeatedly to the same type has no additional effect. +// CHECK:STDERR: fn F(p: const i32**) -> const (const i32)** { +// CHECK:STDERR: ^ +fn F(p: const i32**) -> const (const i32)** { + return p; +} diff --git a/toolchain/semantics/testdata/const/fail_collapse.carbon b/toolchain/semantics/testdata/const/fail_collapse.carbon new file mode 100644 index 000000000000..92281cb78f80 --- /dev/null +++ b/toolchain/semantics/testdata/const/fail_collapse.carbon @@ -0,0 +1,72 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type5, body: {block4}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: G, +// CHECK:STDOUT: p, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: ConstType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type2, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+4, type: type3}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type4, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: nodeError}, +// 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+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +// CHECK:STDERR: fail_collapse.carbon:[[@LINE+3]]:9: `const` applied repeatedly to the same type has no additional effect. +// CHECK:STDERR: fn G(p: const (const i32)**) -> i32** { +// CHECK:STDERR: ^ +fn G(p: const (const i32)**) -> i32** { + // CHECK:STDERR: fail_collapse.carbon:[[@LINE+3]]:11: Cannot implicitly convert from `const i32**` to `i32**`. + // CHECK:STDERR: return p; + // CHECK:STDERR: ^ + return p; +} diff --git a/toolchain/semantics/testdata/pointer/basic.carbon b/toolchain/semantics/testdata/pointer/basic.carbon new file mode 100644 index 000000000000..7132dec1591c --- /dev/null +++ b/toolchain/semantics/testdata/pointer/basic.carbon @@ -0,0 +1,68 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block0, return_type: type0, body: {block2}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 0, +// CHECK:STDOUT: 0, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: n, +// CHECK:STDOUT: p, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: VarStorage, type: type0}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+1, type: type0}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type0}, +// CHECK:STDOUT: {kind: Assign, arg0: node+1, arg1: node+3}, +// 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: IntegerLiteral, arg0: int1, type: type0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+8}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: node+2, +// 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+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F() -> i32 { + var n: i32 = 0; + var p: i32*; + + // TODO: Test taking address and dereference. + // p = &n; + // *p = 1; + // return *p; + + return 0; +} diff --git a/toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon b/toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon new file mode 100644 index 000000000000..29f301bedea1 --- /dev/null +++ b/toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon @@ -0,0 +1,70 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type4, body: {block4}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: ConstMismatch, +// CHECK:STDOUT: p, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: StructType, arg0: block0, type: typeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block0, type: type0}, +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type1, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type2}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+4, type: type2}, +// CHECK:STDOUT: {kind: StructValue, arg0: block0, type: type0}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: ConstType, arg0: type3, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: nodeError}, +// 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+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn ConstMismatch(p: const {}*) -> const ({}*) { + // CHECK:STDERR: fail_type_mismatch.carbon:[[@LINE+3]]:11: Cannot implicitly convert from `const {}*` to `const ({}*)`. + // CHECK:STDERR: return p; + // CHECK:STDERR: ^ + return p; +} diff --git a/toolchain/semantics/testdata/pointer/types.carbon b/toolchain/semantics/testdata/pointer/types.carbon new file mode 100644 index 000000000000..a6b5d8ae2143 --- /dev/null +++ b/toolchain/semantics/testdata/pointer/types.carbon @@ -0,0 +1,85 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type1, body: {block4}}}, +// CHECK:STDOUT: {name: str2, param_refs: block6, return_type: type3, body: {block7}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: Ptr, +// CHECK:STDOUT: p, +// CHECK:STDOUT: ConstPtr, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+1, type: type1}, +// CHECK:STDOUT: {kind: PointerType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+1}, +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type2, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type3}, +// CHECK:STDOUT: {kind: BindName, arg0: str1, arg1: node+8, type: type3}, +// CHECK:STDOUT: {kind: ConstType, arg0: type0, type: typeTypeType}, +// CHECK:STDOUT: {kind: PointerType, arg0: type2, type: typeTypeType}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+8}, +// 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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+13, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn Ptr(p: i32*) -> i32* { + return p; +} + +fn ConstPtr(p: const i32*) -> (const i32)* { + return p; +} diff --git a/toolchain/semantics/testdata/struct/fail_assign_empty.carbon b/toolchain/semantics/testdata/struct/fail_assign_empty.carbon index 6b385c5264be..771eb37ff976 100644 --- a/toolchain/semantics/testdata/struct/fail_assign_empty.carbon +++ b/toolchain/semantics/testdata/struct/fail_assign_empty.carbon @@ -47,7 +47,7 @@ // CHECK:STDOUT: ], // CHECK:STDOUT: ] -// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+3]]:22: Cannot implicitly convert from `{} as Type` to `{.a: i32}`. +// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+3]]:22: Cannot implicitly convert from `{} as type` to `{.a: i32}`. // CHECK:STDERR: var x: {.a: i32} = {}; // CHECK:STDERR: ^ var x: {.a: i32} = {}; diff --git a/toolchain/semantics/testdata/struct/fail_assign_nested.carbon b/toolchain/semantics/testdata/struct/fail_assign_nested.carbon new file mode 100644 index 000000000000..1e780a15ad7a --- /dev/null +++ b/toolchain/semantics/testdata/struct/fail_assign_nested.carbon @@ -0,0 +1,69 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: x, +// CHECK:STDOUT: a, +// CHECK:STDOUT: b, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: ] +// CHECK:STDOUT: type_blocks: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: StructType, arg0: block0, type: typeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block0, type: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str1, arg1: type0}, +// CHECK:STDOUT: {kind: StructType, arg0: block2, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type1}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+4, type: type1}, +// CHECK:STDOUT: {kind: StructValue, arg0: block0, type: type0}, +// CHECK:STDOUT: {kind: StructTypeField, arg0: str2, arg1: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+6, type: type0}, +// CHECK:STDOUT: {kind: StructType, arg0: block3, type: typeTypeType}, +// CHECK:STDOUT: {kind: StructValue, arg0: block4, type: type2}, +// CHECK:STDOUT: {kind: Assign, arg0: node+4, arg1: nodeError}, +// 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+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: node+10, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+7, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+3]]:28: Cannot implicitly convert from `{.b: {}}` to `{.a: {}}`. +// CHECK:STDERR: var x: {.a: {}} = {.b = {}}; +// CHECK:STDERR: ^ +var x: {.a: {}} = {.b = {}}; diff --git a/toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon b/toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon index b4d2b078ce38..6d63c72ab640 100644 --- a/toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon +++ b/toolchain/semantics/testdata/struct/fail_assign_to_empty.carbon @@ -56,7 +56,7 @@ // CHECK:STDOUT: ], // CHECK:STDOUT: ] -// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+3]]:21: Cannot implicitly convert from `{.a: i32}` to `{} as Type`. +// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+3]]:21: Cannot implicitly convert from `{.a: i32}` to `{} as type`. // CHECK:STDERR: var x: {} = {.a = 1}; // CHECK:STDERR: ^ var x: {} = {.a = 1}; diff --git a/toolchain/semantics/testdata/tuples/fail_assign_nested.carbon b/toolchain/semantics/testdata/tuples/fail_assign_nested.carbon new file mode 100644 index 000000000000..e5242c79b3d9 --- /dev/null +++ b/toolchain/semantics/testdata/tuples/fail_assign_nested.carbon @@ -0,0 +1,166 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: 1, +// CHECK:STDOUT: 2, +// CHECK:STDOUT: 3, +// CHECK:STDOUT: 4, +// CHECK:STDOUT: 5, +// CHECK:STDOUT: 6, +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: x, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+9, +// CHECK:STDOUT: nodeIntegerType, +// CHECK:STDOUT: node+11, +// CHECK:STDOUT: node+12, +// CHECK:STDOUT: node+21, +// CHECK:STDOUT: node+32, +// 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: type2, +// CHECK:STDOUT: type2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: type3, +// CHECK:STDOUT: type3, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: type2, +// CHECK:STDOUT: type2, +// CHECK:STDOUT: type2, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: type5, +// CHECK:STDOUT: type5, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// 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: block2, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+3, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType}, +// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block4, type: type0}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+7, type: type0}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block3, type: type1}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock2, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock3, type: typeTypeType}, +// CHECK:STDOUT: {kind: VarStorage, type: type4}, +// CHECK:STDOUT: {kind: BindName, arg0: str0, arg1: node+13, type: type4}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int0, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+15, type: type2}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int1, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+17, type: type2}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int2, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+19, type: type2}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock4, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block5, type: type5}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+22, type: type5}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int3, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+24, type: type2}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int4, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+26, type: type2}, +// CHECK:STDOUT: {kind: IntegerLiteral, arg0: int5, type: type2}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+28, type: type2}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block7, type: type5}, +// CHECK:STDOUT: {kind: StubReference, arg0: node+30, type: type5}, +// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock5, type: typeTypeType}, +// CHECK:STDOUT: {kind: TupleValue, arg0: block6, type: type6}, +// CHECK:STDOUT: {kind: Assign, arg0: node+13, arg1: nodeError}, +// 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+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+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: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+4, +// CHECK:STDOUT: node+8, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+5, +// CHECK:STDOUT: node+6, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+16, +// CHECK:STDOUT: node+18, +// CHECK:STDOUT: node+20, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+23, +// CHECK:STDOUT: node+31, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+25, +// CHECK:STDOUT: node+27, +// CHECK:STDOUT: node+29, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +// CHECK:STDERR: fail_assign_nested.carbon:[[@LINE+3]]:57: Cannot implicitly convert from `((i32, i32, i32), (i32, i32, i32)) as type` to `((i32, i32), (i32, i32)) as type`. +// CHECK:STDERR: var x: ((i32, i32), (i32, i32)) = ((1, 2, 3), (4, 5, 6)); +// CHECK:STDERR: ^ +var x: ((i32, i32), (i32, i32)) = ((1, 2, 3), (4, 5, 6));