Skip to content

Commit

Permalink
Track the type as written in BaseDecl and AdaptDecl. (carbon-lang…
Browse files Browse the repository at this point in the history
…uage#4564)

Represent the type as an `InstId` rather than as a `TypeId` to preserve
how it was written and better support tracking its value in a generic.
Add accessors to `Class` to get the base and adapted type to reduce code
duplication, and add `TypeStore::GetObjectRepr` to make it easier to map
from a type to its possibly-adapted object representation type. In
passing, also move `GetIntTypeInfo` and `GetUnqualifiedType` into
`TypeStore`.

This fixes specifics of generic adapters to properly look at the
specific adapted type, and also fixes importing of adapters.

---------

Co-authored-by: Jon Ross-Perkins <[email protected]>
  • Loading branch information
2 people authored and bricknerb committed Nov 28, 2024
1 parent bf916c9 commit a773c76
Show file tree
Hide file tree
Showing 51 changed files with 1,530 additions and 415 deletions.
42 changes: 20 additions & 22 deletions toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,18 +390,19 @@ static auto DiagnoseInvalidQualifiedNameAccess(Context& context, SemIRLoc loc,
}

// TODO: Support scoped entities other than just classes.
auto class_info = context.classes().Get(class_type->class_id);
const auto& class_info = context.classes().Get(class_type->class_id);

auto parent_type_id = class_info.self_type_id;

if (access_kind == SemIR::AccessKind::Private && is_parent_access) {
if (auto base_decl = context.insts().TryGetAsIfValid<SemIR::BaseDecl>(
class_info.base_id)) {
parent_type_id = base_decl->base_type_id;
} else if (auto adapt_decl =
context.insts().TryGetAsIfValid<SemIR::AdaptDecl>(
class_info.adapt_id)) {
parent_type_id = adapt_decl->adapted_type_id;
if (auto base_type_id =
class_info.GetBaseType(context.sem_ir(), class_type->specific_id);
base_type_id.is_valid()) {
parent_type_id = base_type_id;
} else if (auto adapted_type_id = class_info.GetAdaptedType(
context.sem_ir(), class_type->specific_id);
adapted_type_id.is_valid()) {
parent_type_id = adapted_type_id;
} else {
CARBON_FATAL("Expected parent for parent access");
}
Expand Down Expand Up @@ -957,7 +958,13 @@ class TypeCompleter {
if (inst.specific_id.is_valid()) {
ResolveSpecificDefinition(context_, inst.specific_id);
}
Push(class_info.GetObjectRepr(context_.sem_ir(), inst.specific_id));
if (auto adapted_type_id =
class_info.GetAdaptedType(context_.sem_ir(), inst.specific_id);
adapted_type_id.is_valid()) {
Push(adapted_type_id);
} else {
Push(class_info.GetObjectRepr(context_.sem_ir(), inst.specific_id));
}
break;
}
case CARBON_KIND(SemIR::ConstType inst): {
Expand Down Expand Up @@ -1127,12 +1134,10 @@ class TypeCompleter {
auto& class_info = context_.classes().Get(inst.class_id);
// The value representation of an adapter is the value representation of
// its adapted type.
if (class_info.adapt_id.is_valid()) {
return GetNestedValueRepr(SemIR::GetTypeInSpecific(
context_.sem_ir(), inst.specific_id,
context_.insts()
.GetAs<SemIR::AdaptDecl>(class_info.adapt_id)
.adapted_type_id));
if (auto adapted_type_id =
class_info.GetAdaptedType(context_.sem_ir(), inst.specific_id);
adapted_type_id.is_valid()) {
return GetNestedValueRepr(adapted_type_id);
}
// Otherwise, the value representation for a class is a pointer to the
// object representation.
Expand Down Expand Up @@ -1400,13 +1405,6 @@ auto Context::GetUnboundElementType(SemIR::TypeId class_type_id,
element_type_id);
}

auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId {
if (auto const_type = types().TryGetAs<SemIR::ConstType>(type_id)) {
return const_type->inner_id;
}
return type_id;
}

auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
output << "Check::Context\n";

Expand Down
3 changes: 0 additions & 3 deletions toolchain/check/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,6 @@ class Context {
auto GetUnboundElementType(SemIR::TypeId class_type_id,
SemIR::TypeId element_type_id) -> SemIR::TypeId;

// Removes any top-level `const` qualifiers from a type.
auto GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId;

// Adds an exported name.
auto AddExport(SemIR::InstId inst_id) -> void { exports_.push_back(inst_id); }

Expand Down
20 changes: 10 additions & 10 deletions toolchain/check/convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,15 +607,12 @@ static auto ComputeInheritancePath(Context& context, SemIR::TypeId derived_id,
break;
}
auto& derived_class = context.classes().Get(derived_class_type->class_id);
if (!derived_class.base_id.is_valid()) {
auto base_type_id = derived_class.GetBaseType(
context.sem_ir(), derived_class_type->specific_id);
if (!base_type_id.is_valid()) {
result = std::nullopt;
break;
}
auto base_decl =
context.insts().GetAs<SemIR::BaseDecl>(derived_class.base_id);
auto base_type_id = SemIR::GetTypeInSpecific(
context.sem_ir(), derived_class_type->specific_id,
base_decl.base_type_id);
result->push_back({derived_class.base_id, base_type_id});
derived_id = base_type_id;
}
Expand Down Expand Up @@ -712,12 +709,15 @@ static auto GetCompatibleBaseType(Context& context, SemIR::TypeId type_id)
-> SemIR::TypeId {
// If the type is an adapter, its object representation type is its compatible
// non-adapter type.
if (auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id)) {
while (auto class_type =
context.types().TryGetAs<SemIR::ClassType>(type_id)) {
auto& class_info = context.classes().Get(class_type->class_id);
if (class_info.adapt_id.is_valid()) {
return class_info.GetObjectRepr(context.sem_ir(),
class_type->specific_id);
auto adapted_type_id =
class_info.GetAdaptedType(context.sem_ir(), class_type->specific_id);
if (!adapted_type_id.is_valid()) {
break;
}
type_id = adapted_type_id;
}

// Otherwise, the type itself is a non-adapter type.
Expand Down
12 changes: 7 additions & 5 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ static auto PerformCheckedIntConvert(Context& context, SemIRLoc loc,
auto arg_val = context.ints().Get(arg.int_id);

auto [is_signed, bit_width_id] =
context.sem_ir().GetIntTypeInfo(dest_type_id);
context.sem_ir().types().GetIntTypeInfo(dest_type_id);
auto width = bit_width_id.is_valid()
? context.ints().Get(bit_width_id).getZExtValue()
: arg_val.getBitWidth();
Expand Down Expand Up @@ -718,7 +718,8 @@ static auto PerformBuiltinUnaryIntOp(Context& context, SemIRLoc loc,
SemIR::InstId arg_id)
-> SemIR::ConstantId {
auto op = context.insts().GetAs<SemIR::IntValue>(arg_id);
auto [is_signed, bit_width_id] = context.sem_ir().GetIntTypeInfo(op.type_id);
auto [is_signed, bit_width_id] =
context.sem_ir().types().GetIntTypeInfo(op.type_id);
CARBON_CHECK(bit_width_id != IntId::Invalid,
"Cannot evaluate a generic bit width integer: {0}", op);
llvm::APInt op_val = context.ints().GetAtWidth(op.int_id, bit_width_id);
Expand Down Expand Up @@ -771,7 +772,7 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc,
}

auto [lhs_is_signed, lhs_bit_width_id] =
context.sem_ir().GetIntTypeInfo(lhs.type_id);
context.sem_ir().types().GetIntTypeInfo(lhs.type_id);
llvm::APInt lhs_val = context.ints().GetAtWidth(lhs.int_id, lhs_bit_width_id);

llvm::APInt result_val;
Expand Down Expand Up @@ -916,7 +917,8 @@ static auto PerformBuiltinIntComparison(Context& context,
CARBON_CHECK(lhs.type_id == rhs.type_id,
"Builtin comparison with mismatched types!");

auto [is_signed, bit_width_id] = context.sem_ir().GetIntTypeInfo(lhs.type_id);
auto [is_signed, bit_width_id] =
context.sem_ir().types().GetIntTypeInfo(lhs.type_id);
CARBON_CHECK(bit_width_id != IntId::Invalid,
"Cannot evaluate a generic bit width integer: {0}", lhs);
llvm::APInt lhs_val = context.ints().GetAtWidth(lhs.int_id, bit_width_id);
Expand Down Expand Up @@ -1497,6 +1499,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
// TODO: This doesn't properly handle redeclarations. Consider adding a
// corresponding `Value` inst for each of these cases, or returning the
// first declaration.
case SemIR::AdaptDecl::Kind:
case SemIR::AssociatedConstantDecl::Kind:
case SemIR::BaseDecl::Kind:
case SemIR::FieldDecl::Kind:
Expand Down Expand Up @@ -1699,7 +1702,6 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
}

// These cases are either not expressions or not constant.
case SemIR::AdaptDecl::Kind:
case SemIR::AddrPattern::Kind:
case SemIR::Assign::Kind:
case SemIR::BindName::Kind:
Expand Down
60 changes: 26 additions & 34 deletions toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/decl_name_stack.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/handle.h"
Expand Down Expand Up @@ -387,21 +388,23 @@ auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool {
[&] {
CARBON_DIAGNOSTIC(IncompleteTypeInAdaptDecl, Error,
"adapted type {0} is an incomplete type",
SemIR::TypeId);
InstIdAsType);
return context.emitter().Build(node_id, IncompleteTypeInAdaptDecl,
adapted_type_id);
adapted_inst_id);
},
[&] {
CARBON_DIAGNOSTIC(AbstractTypeInAdaptDecl, Error,
"adapted type {0} is an abstract type",
SemIR::TypeId);
"adapted type {0} is an abstract type", InstIdAsType);
return context.emitter().Build(node_id, AbstractTypeInAdaptDecl,
adapted_type_id);
adapted_inst_id);
});
if (adapted_type_id == SemIR::TypeId::Error) {
adapted_inst_id = SemIR::InstId::BuiltinErrorInst;
}

// Build a SemIR representation for the declaration.
class_info.adapt_id = context.AddInst<SemIR::AdaptDecl>(
node_id, {.adapted_type_id = adapted_type_id});
node_id, {.adapted_type_inst_id = adapted_inst_id});

// Extend the class scope with the adapted type's scope if requested.
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
Expand Down Expand Up @@ -432,9 +435,10 @@ struct BaseInfo {
SemIR::NameScopeId scope_id;
SemIR::InstId inst_id;
};
constexpr BaseInfo BaseInfo::Error = {.type_id = SemIR::TypeId::Error,
.scope_id = SemIR::NameScopeId::Invalid,
.inst_id = SemIR::InstId::Invalid};
constexpr BaseInfo BaseInfo::Error = {
.type_id = SemIR::TypeId::Error,
.scope_id = SemIR::NameScopeId::Invalid,
.inst_id = SemIR::InstId::BuiltinErrorInst};
} // namespace

// Diagnoses an attempt to derive from a final type.
Expand Down Expand Up @@ -531,7 +535,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool {
context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
class_info.base_id = context.AddInst<SemIR::BaseDecl>(
node_id, {.type_id = field_type_id,
.base_type_id = base_info.type_id,
.base_type_inst_id = base_info.inst_id,
.index = SemIR::ElementIndex::Invalid});

if (base_info.type_id != SemIR::TypeId::Error) {
Expand Down Expand Up @@ -607,27 +611,17 @@ static auto CheckCompleteAdapterClassType(Context& context,
}

// The object representation of the adapter is the object representation
// of the adapted type. This is the adapted type itself unless it's a class
// type.
//
// TODO: The object representation of `const T` should also be the object
// representation of `T`.
auto adapted_type_id = context.insts()
.GetAs<SemIR::AdaptDecl>(class_info.adapt_id)
.adapted_type_id;
if (auto adapted_class =
context.types().TryGetAs<SemIR::ClassType>(adapted_type_id)) {
auto& adapted_class_info = context.classes().Get(adapted_class->class_id);
if (adapted_class_info.adapt_id.is_valid()) {
return adapted_class_info.complete_type_witness_id;
}
}
// of the adapted type.
auto adapted_type_id =
class_info.GetAdaptedType(context.sem_ir(), SemIR::SpecificId::Invalid);
auto object_repr_id = context.types().GetObjectRepr(adapted_type_id);

return context.AddInst<SemIR::CompleteTypeWitness>(
node_id,
{.type_id = context.GetBuiltinType(SemIR::BuiltinInstKind::WitnessType),
.object_repr_id = adapted_type_id});
.object_repr_id = object_repr_id});
}

static auto AddStructTypeFields(
Context& context,
llvm::SmallVector<SemIR::StructTypeField>& struct_type_fields)
Expand Down Expand Up @@ -664,11 +658,12 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
}

bool defining_vptr = class_info.is_dynamic;
if (class_info.base_id.is_valid()) {
auto base_info = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
auto base_type_id =
class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::Invalid);
if (base_type_id.is_valid()) {
// TODO: If the base class is template dependent, we will need to decide
// whether to add a vptr as part of instantiation.
if (auto* base_class_info = TryGetAsClass(context, base_info.base_type_id);
if (auto* base_class_info = TryGetAsClass(context, base_type_id);
base_class_info && base_class_info->is_dynamic) {
defining_vptr = false;
}
Expand All @@ -684,16 +679,13 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
.type_id = context.GetPointerType(
context.GetBuiltinType(SemIR::BuiltinInstKind::VtableType))});
}
if (class_info.base_id.is_valid()) {
if (base_type_id.is_valid()) {
auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
base_decl.index =
SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
context.ReplaceInstPreservingConstantValue(class_info.base_id, base_decl);
struct_type_fields.push_back(
{.name_id = SemIR::NameId::Base,
.type_id = context.insts()
.GetAs<SemIR::BaseDecl>(class_info.base_id)
.base_type_id});
{.name_id = SemIR::NameId::Base, .type_id = base_type_id});
}

return context.AddInst<SemIR::CompleteTypeWitness>(
Expand Down
Loading

0 comments on commit a773c76

Please sign in to comment.