Files
carbon-lang/toolchain/sem_ir/generic.cpp
T
Richard Smith 2044366652 Support initialization of specific classes from struct literals (#4320)
Add support for initializing types like `GenericClass(i32)` from a
struct literal. A new kind of instruction, `complete_type_witness`, is
added to the class definition to track the object representation type so
that it's visible to the generics machinery. Accesses to the object
representation of a class have all been updated to pass in the class's
`SpecificId` so that the types of the fields of the specific class are
used instead of the types of the fields of the generic class in places
that look at the object representation -- primarily class
initialization.
2024-09-19 19:18:32 +00:00

101 lines
3.4 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/sem_ir/generic.h"
#include "toolchain/sem_ir/file.h"
namespace Carbon::SemIR {
class SpecificStore::KeyContext : public TranslatingKeyContext<KeyContext> {
public:
// A lookup key for a specific.
struct Key {
GenericId generic_id;
InstBlockId args_id;
friend auto operator==(const Key&, const Key&) -> bool = default;
};
explicit KeyContext(llvm::ArrayRef<Specific> specifics)
: specifics_(specifics) {}
auto TranslateKey(SpecificId id) const -> Key {
const auto& specific = specifics_[id.index];
return {.generic_id = specific.generic_id, .args_id = specific.args_id};
}
private:
llvm::ArrayRef<Specific> specifics_;
};
auto SpecificStore::GetOrAdd(GenericId generic_id, InstBlockId args_id)
-> SpecificId {
CARBON_CHECK(generic_id.is_valid());
return lookup_table_
.Insert(
KeyContext::Key{.generic_id = generic_id, .args_id = args_id},
[&] {
return specifics_.Add(
{.generic_id = generic_id, .args_id = args_id});
},
KeyContext(specifics_.array_ref()))
.key();
}
auto SpecificStore::CollectMemUsage(MemUsage& mem_usage,
llvm::StringRef label) const -> void {
mem_usage.Collect(MemUsage::ConcatLabel(label, "specifics_"), specifics_);
mem_usage.Add(MemUsage::ConcatLabel(label, "lookup_table_"), lookup_table_,
KeyContext(specifics_.array_ref()));
}
auto GetConstantInSpecific(const File& sem_ir, SpecificId specific_id,
ConstantId const_id) -> ConstantId {
if (!const_id.is_symbolic()) {
// Type does not depend on a generic parameter.
return const_id;
}
const auto& symbolic = sem_ir.constant_values().GetSymbolicConstant(const_id);
if (!symbolic.generic_id.is_valid()) {
// Constant is an abstract symbolic constant, not associated with some
// particular generic.
return const_id;
}
if (!specific_id.is_valid()) {
// We have a generic constant but no specific. We treat as a request for the
// canonical value of the constant.
return sem_ir.constant_values().Get(symbolic.inst_id);
}
const auto& specific = sem_ir.specifics().Get(specific_id);
CARBON_CHECK(specific.generic_id == symbolic.generic_id,
"Given a specific for the wrong generic");
auto value_block_id = specific.GetValueBlock(symbolic.index.region());
CARBON_CHECK(
value_block_id.is_valid(),
"Queried {0} in {1} for {2} before it was resolved.", symbolic.index,
specific_id,
sem_ir.insts().Get(sem_ir.generics().Get(specific.generic_id).decl_id));
return sem_ir.constant_values().Get(
sem_ir.inst_blocks().Get(value_block_id)[symbolic.index.index()]);
}
auto GetConstantValueInSpecific(const File& sem_ir, SpecificId specific_id,
InstId inst_id) -> ConstantId {
return GetConstantInSpecific(sem_ir, specific_id,
sem_ir.constant_values().Get(inst_id));
}
auto GetTypeInSpecific(const File& sem_ir, SpecificId specific_id,
TypeId type_id) -> TypeId {
return TypeId::ForTypeConstant(GetConstantInSpecific(
sem_ir, specific_id, sem_ir.types().GetConstantId(type_id)));
}
} // namespace Carbon::SemIR