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.
This commit is contained in:
Jon Ross-Perkins
2023-05-26 14:42:45 -07:00
committed by GitHub
parent 1497e1333d
commit 2e4beaf8f0
16 changed files with 133 additions and 161 deletions
+33 -37
View File
@@ -282,19 +282,7 @@ auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id,
}
}
if (value_type_id != SemanticsTypeId::TypeType &&
as_type_id != SemanticsTypeId::TypeType) {
auto value_type = semantics_->GetNode(semantics_->GetType(value_type_id));
auto as_type = semantics_->GetNode(semantics_->GetType(as_type_id));
if (CanImplicitAsStruct(value_type, as_type)) {
// Under the current implementation, struct types are only allowed to
// ImplicitAs when they're equivalent. What's really missing is type
// consolidation such that this would fall under the above `value_type_id
// == as_type_id` case. In the future, this will need to handle actual
// conversions.
return ImplicitAsKind::Identical;
}
}
// TODO: Handle ImplicitAs for compatible structs and tuples.
if (output_value_id != nullptr) {
*output_value_id = SemanticsNodeId::BuiltinInvalidType;
@@ -302,30 +290,6 @@ auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id,
return ImplicitAsKind::Incompatible;
}
auto SemanticsContext::CanImplicitAsStruct(SemanticsNode value_type,
SemanticsNode as_type) -> bool {
if (value_type.kind() != SemanticsNodeKind::StructType ||
as_type.kind() != SemanticsNodeKind::StructType) {
return false;
}
auto value_type_refs = semantics_->GetNodeBlock(value_type.GetAsStructType());
auto as_type_refs = semantics_->GetNodeBlock(as_type.GetAsStructType());
if (value_type_refs.size() != as_type_refs.size()) {
return false;
}
for (int i = 0; i < static_cast<int>(value_type_refs.size()); ++i) {
auto value_type_field = semantics_->GetNode(value_type_refs[i]);
auto as_type_field = semantics_->GetNode(as_type_refs[i]);
if (value_type_field.type_id() != as_type_field.type_id() ||
value_type_field.GetAsStructTypeField() !=
as_type_field.GetAsStructTypeField()) {
return false;
}
}
return true;
}
auto SemanticsContext::ParamOrArgStart() -> void {
params_or_args_stack_.Push();
}
@@ -380,6 +344,38 @@ auto SemanticsContext::CanonicalizeType(SemanticsNodeId node_id)
return type_id;
}
auto SemanticsContext::CanonicalizeStructType(ParseTree::Node parse_node,
SemanticsNodeBlockId refs_id)
-> SemanticsTypeId {
// Construct the field structure for lookup.
auto refs = semantics_->GetNodeBlock(refs_id);
llvm::FoldingSetNodeID canonical_id;
for (const auto& ref_id : refs) {
auto ref = semantics_->GetNode(ref_id);
canonical_id.AddInteger(ref.GetAsStructTypeField().index);
canonical_id.AddInteger(ref.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_->AddType(node_id);
CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second);
canonical_struct_types_nodes_.push_back(
std::make_unique<StructTypeNode>(canonical_id, type_id));
canonical_struct_types_.InsertNode(canonical_struct_types_nodes_.back().get(),
insert_pos);
return type_id;
}
auto SemanticsContext::PrintForStackDump(llvm::raw_ostream& output) const
-> void {
node_stack_.PrintForStackDump(output);