From c8b42379a4849b32dd1d8a3a580b09edd8b9bd68 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 2 Aug 2023 16:19:52 -0700 Subject: [PATCH] Basic semantic checking for pointer types and const-qualified types. (#3038) Semantic handling for use of `T*` and `const T` as types. There's no way to form values of these types yet, and no conversions for them are supported. Factor out the common code to canonicalize types using a folding set, and switch to using the same folding set for all kinds of type by adding the kind as part of the folding set key. Improve type printing to not include the `as type` portion when the type is printed in a context within another type where a conversion to `type` is implied, as in `{}*` and pre-existing cases like `({}, {}) as type` (which we used to print as `({} as type, {} as type}) as type`. --- toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/lowering/lowering_handle_type.cpp | 12 ++ toolchain/semantics/BUILD | 5 +- toolchain/semantics/semantics_context.cpp | 156 ++++++++++------ toolchain/semantics/semantics_context.h | 49 ++++-- .../semantics/semantics_handle_operator.cpp | 49 +++++- toolchain/semantics/semantics_ir.cpp | 102 ++++++++++- toolchain/semantics/semantics_node.h | 6 + toolchain/semantics/semantics_node_kind.def | 2 + toolchain/semantics/semantics_node_kind.h | 5 + toolchain/semantics/semantics_node_stack.h | 1 + .../semantics/testdata/const/collapse.carbon | 70 ++++++++ .../testdata/const/fail_collapse.carbon | 72 ++++++++ .../semantics/testdata/pointer/basic.carbon | 68 +++++++ .../pointer/fail_type_mismatch.carbon | 70 ++++++++ .../semantics/testdata/pointer/types.carbon | 85 +++++++++ .../testdata/struct/fail_assign_empty.carbon | 2 +- .../testdata/struct/fail_assign_nested.carbon | 69 ++++++++ .../struct/fail_assign_to_empty.carbon | 2 +- .../testdata/tuples/fail_assign_nested.carbon | 166 ++++++++++++++++++ 20 files changed, 905 insertions(+), 87 deletions(-) create mode 100644 toolchain/semantics/testdata/const/collapse.carbon create mode 100644 toolchain/semantics/testdata/const/fail_collapse.carbon create mode 100644 toolchain/semantics/testdata/pointer/basic.carbon create mode 100644 toolchain/semantics/testdata/pointer/fail_type_mismatch.carbon create mode 100644 toolchain/semantics/testdata/pointer/types.carbon create mode 100644 toolchain/semantics/testdata/struct/fail_assign_nested.carbon create mode 100644 toolchain/semantics/testdata/tuples/fail_assign_nested.carbon 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));