Files
carbon-lang/toolchain/semantics/semantics_handle_struct.cpp
T
Jon Ross-Perkins 2e4beaf8f0 Canonicalize struct types. (#2855)
This adds canonicalization of struct types based on their type fields. It obsoletes the current CanImplicitAsStruct because the type ids should now be identical when they're structurally identical; there's only a reason to implicit CanImplicitAsStruct to detect _compatible_ conversions.

The type fields themselves aren't canonicalized because it would need to be done during the first parse, and could yield name conflicts being associated with the wrong location. i.e.:

```
var x: {a: i32, a: i32};
var y: {a: i32, b: i32, a: i32};
```

This should yield two separate name conflict diagnostics pointing at the type fields for each respective line, but if struct type fields were canonicalized then both would point at the first `a: i32` field definition. This isn't expected to be an issue for types because I'm trying to print those, but we may also end up with a "first defined at" situation in some cases (still, less confusing because the type should match). Regardless, I think individual fields gets much more awkward.
2023-05-26 14:42:45 -07:00

117 lines
4.3 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/semantics/semantics_context.h"
namespace Carbon {
auto SemanticsHandleStructComma(SemanticsContext& context,
ParseTree::Node /*parse_node*/) -> bool {
context.ParamOrArgComma(
/*for_args=*/context.parse_tree().node_kind(
context.node_stack().PeekParseNode()) !=
ParseNodeKind::StructFieldType);
return true;
}
auto SemanticsHandleStructFieldDesignator(SemanticsContext& context,
ParseTree::Node /*parse_node*/)
-> bool {
// This leaves the designated name on top because the `.` isn't interesting.
CARBON_CHECK(
context.parse_tree().node_kind(context.node_stack().PeekParseNode()) ==
ParseNodeKind::DesignatedName);
return true;
}
auto SemanticsHandleStructFieldType(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
auto [type_node, type_id] = context.node_stack().PopForParseNodeAndNodeId();
SemanticsTypeId cast_type_id = context.ExpressionAsType(type_node, type_id);
auto [name_node, name_id] = context.node_stack().PopForParseNodeAndNameId(
ParseNodeKind::DesignatedName);
context.AddNode(
SemanticsNode::StructTypeField::Make(name_node, cast_type_id, name_id));
context.node_stack().Push(parse_node);
return true;
}
auto SemanticsHandleStructFieldUnknown(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleStructFieldUnknown");
}
auto SemanticsHandleStructFieldValue(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
auto [value_parse_node, value_node_id] =
context.node_stack().PopForParseNodeAndNodeId();
auto [_, name_id] = context.node_stack().PopForParseNodeAndNameId(
ParseNodeKind::DesignatedName);
// Store the name for the type.
auto type_block_id = context.args_type_info_stack().PeekForAdd();
context.semantics().AddNode(
type_block_id,
SemanticsNode::StructTypeField::Make(
parse_node, context.semantics().GetNode(value_node_id).type_id(),
name_id));
// Push the value back on the stack as an argument.
context.node_stack().Push(parse_node, value_node_id);
return true;
}
auto SemanticsHandleStructLiteral(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
auto refs_id = context.ParamOrArgEnd(
/*for_args=*/true, ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
context.PopScope();
context.node_stack().PopAndDiscardSoloParseNode(
ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
auto type_block_id = context.args_type_info_stack().Pop();
auto type_id = context.CanonicalizeStructType(parse_node, type_block_id);
auto value_id = context.AddNode(
SemanticsNode::StructValue::Make(parse_node, type_id, refs_id));
context.node_stack().Push(parse_node, value_id);
return true;
}
auto SemanticsHandleStructLiteralOrStructTypeLiteralStart(
SemanticsContext& context, ParseTree::Node parse_node) -> bool {
context.PushScope();
context.node_stack().Push(parse_node);
// At this point we aren't sure whether this will be a value or type literal,
// so we push onto args irrespective. It just won't be used for a type
// literal.
context.args_type_info_stack().Push();
context.ParamOrArgStart();
return true;
}
auto SemanticsHandleStructTypeLiteral(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
auto refs_id = context.ParamOrArgEnd(
/*for_args=*/false, ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
context.PopScope();
context.node_stack().PopAndDiscardSoloParseNode(
ParseNodeKind::StructLiteralOrStructTypeLiteralStart);
// This is only used for value literals.
context.args_type_info_stack().Pop();
CARBON_CHECK(refs_id != SemanticsNodeBlockId::Empty)
<< "{} is handled by StructLiteral.";
auto type_id = context.CanonicalizeStructType(parse_node, refs_id);
context.node_stack().Push(parse_node, context.semantics().GetType(type_id));
return true;
}
} // namespace Carbon