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`.
This commit is contained in:
Richard Smith
2023-08-02 23:19:52 +00:00
committed by GitHub
parent e448ea5a7c
commit c8b42379a4
20 changed files with 905 additions and 87 deletions
+102 -54
View File
@@ -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<void(llvm::FoldingSetNodeID& canonical_id)> profile_type,
llvm::function_ref<SemanticsNodeId()> 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<TypeNode>(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<TypeNode>(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<SemanticsTypeId>&& 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<TypeNode>(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;
}