Add GenericInstance type to represent instances of generics. (#4085)

Also add a corresponding value store and YAML output.

We don't create any generic instances in this change; this is just
adding infrastructure for future changes.
This commit is contained in:
Richard Smith
2024-06-26 22:44:12 +00:00
committed by GitHub
parent e7b0529957
commit a0d767246f
13 changed files with 135 additions and 0 deletions
+3
View File
@@ -390,6 +390,9 @@ class Context {
auto generics() -> ValueStore<SemIR::GenericId>& {
return sem_ir().generics();
}
auto generic_instances() -> SemIR::GenericInstanceStore& {
return sem_ir().generic_instances();
}
auto import_irs() -> ValueStore<SemIR::ImportIRId>& {
return sem_ir().import_irs();
}
+1
View File
@@ -20,6 +20,7 @@
// CHECK:STDOUT: functions: {}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type_blocks: {}
@@ -33,6 +33,7 @@ fn B() {}
// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: empty, body: [block3]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: template inst+2, value_rep: {kind: none, type: type2}}
@@ -95,6 +96,7 @@ fn B() {}
// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: empty, body: [block3]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: template inst+2, value_rep: {kind: none, type: type2}}
@@ -33,6 +33,7 @@ fn B() {}
// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: empty, body: [block3]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: template inst+2, value_rep: {kind: none, type: type2}}
@@ -74,6 +75,7 @@ fn B() {}
// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: empty, body: [block3]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: template inst+2, value_rep: {kind: none, type: type2}}
@@ -28,6 +28,7 @@ fn Foo(n: ()) -> ((), ()) {
// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: block3, return_storage: inst+13, return_slot: present, body: [block6]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics: {}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: template inst+1, value_rep: {kind: none, type: type1}}
@@ -30,6 +30,7 @@ fn Foo[T:! type](n: T) -> (T, ()) {
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: generics:
// CHECK:STDOUT: generic0: {decl: inst+16, bindings: block7}
// CHECK:STDOUT: generic_instances: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {constant: symbolic inst+3, value_rep: {kind: copy, type: type1}}
+2
View File
@@ -84,6 +84,7 @@ cc_library(
"constant.cpp",
"file.cpp",
"function.cpp",
"generic.cpp",
"name.cpp",
],
hdrs = [
@@ -116,6 +117,7 @@ cc_library(
"//common:enum_base",
"//common:error",
"//common:hashing",
"//common:set",
"//toolchain/base:kind_switch",
"//toolchain/base:value_store",
"//toolchain/base:yaml",
+1
View File
@@ -141,6 +141,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
map.Add("functions", functions_.OutputYaml());
map.Add("classes", classes_.OutputYaml());
map.Add("generics", generics_.OutputYaml());
map.Add("generic_instances", generic_instances_.OutputYaml());
map.Add("types", types_.OutputYaml());
map.Add("type_blocks", type_blocks_.OutputYaml());
map.Add("insts",
+9
View File
@@ -131,6 +131,12 @@ class File : public Printable<File> {
auto impls() const -> const ImplStore& { return impls_; }
auto generics() -> ValueStore<GenericId>& { return generics_; }
auto generics() const -> const ValueStore<GenericId>& { return generics_; }
auto generic_instances() -> GenericInstanceStore& {
return generic_instances_;
}
auto generic_instances() const -> const GenericInstanceStore& {
return generic_instances_;
}
auto import_irs() -> ValueStore<ImportIRId>& { return import_irs_; }
auto import_irs() const -> const ValueStore<ImportIRId>& {
return import_irs_;
@@ -214,6 +220,9 @@ class File : public Printable<File> {
// Storage for generics.
ValueStore<GenericId> generics_;
// Storage for instances of generics.
GenericInstanceStore generic_instances_;
// Related IRs. There are some fixed entries at the start; see ImportIRId.
ValueStore<ImportIRId> import_irs_;
+55
View File
@@ -0,0 +1,55 @@
// 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"
namespace Carbon::SemIR {
class GenericInstanceStore::KeyContext {
public:
// A lookup key for a generic instance.
struct Key {
GenericId generic_id;
InstBlockId args_id;
friend auto operator==(const Key&, const Key&) -> bool = default;
};
explicit KeyContext(llvm::ArrayRef<GenericInstance> instances)
: instances_(instances) {}
auto AsKey(GenericInstanceId id) const -> Key {
const auto& instance = instances_[id.index];
return {.generic_id = instance.generic_id, .args_id = instance.args_id};
}
static auto AsKey(Key key) -> Key { return key; }
template <typename KeyT>
auto HashKey(KeyT key, uint64_t seed) const -> HashCode {
return HashValue(AsKey(key), seed);
}
template <typename LHSKeyT, typename RHSKeyT>
auto KeyEq(const LHSKeyT& lhs_key, const RHSKeyT& rhs_key) const -> bool {
return AsKey(lhs_key) == AsKey(rhs_key);
}
private:
llvm::ArrayRef<GenericInstance> instances_;
};
auto GenericInstanceStore::GetOrAdd(GenericId generic_id, InstBlockId args_id)
-> GenericInstanceId {
return lookup_table_
.Insert(
KeyContext::Key{.generic_id = generic_id, .args_id = args_id},
[&] {
return generic_instances_.Add(
{.generic_id = generic_id, .args_id = args_id});
},
KeyContext(generic_instances_.array_ref()))
.key();
}
} // namespace Carbon::SemIR
+38
View File
@@ -5,6 +5,7 @@
#ifndef CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_
#define CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_
#include "common/set.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::SemIR {
@@ -29,6 +30,43 @@ struct Generic : public Printable<Generic> {
InstBlockId bindings_id;
};
// An instance of a generic entity, such as an instance of a generic function.
// For each construct that depends on a compile-time parameter in the generic
// entity, this contains the corresponding non-generic value. This includes
// values for the compile-time parameters themselves.
struct GenericInstance : Printable<GenericInstance> {
auto Print(llvm::raw_ostream& out) const -> void {
out << "{generic: " << generic_id << ", args: " << args_id << "}";
}
// The generic that this is an instance of.
GenericId generic_id;
// Argument values, corresponding to the bindings in `Generic::bindings_id`.
InstBlockId args_id;
};
// Provides storage for deduplicated instances of generics.
class GenericInstanceStore : public Yaml::Printable<GenericInstanceStore> {
public:
// Adds a new generic instance, or gets the existing generic instance for a
// specified generic and argument list. Returns the ID of the generic
// instance. The argument IDs must be for instructions in the constant block,
// and must be a canonical instruction block ID.
auto GetOrAdd(GenericId generic_id, InstBlockId args_id) -> GenericInstanceId;
// These are to support printable structures, and are not guaranteed.
auto OutputYaml() const -> Yaml::OutputMapping {
return generic_instances_.OutputYaml();
}
private:
// Context for hashing keys.
class KeyContext;
ValueStore<GenericInstanceId> generic_instances_;
Carbon::Set<GenericInstanceId, 0, KeyContext> lookup_table_;
};
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_
+19
View File
@@ -22,6 +22,7 @@ struct BindNameInfo;
struct Class;
struct Function;
struct Generic;
struct GenericInstance;
struct ImportIR;
struct ImportIRInst;
struct Interface;
@@ -300,6 +301,24 @@ struct GenericId : public IdBase, public Printable<GenericId> {
constexpr GenericId GenericId::Invalid = GenericId(InvalidIndex);
// The ID of an instance of a generic.
struct GenericInstanceId : public IdBase, public Printable<GenericInstanceId> {
using ValueType = GenericInstance;
// An explicitly invalid ID. This is typically used to represent a non-generic
// instance.
static const GenericInstanceId Invalid;
using IdBase::IdBase;
auto Print(llvm::raw_ostream& out) const -> void {
out << "genericInstance";
IdBase::Print(out);
}
};
constexpr GenericInstanceId GenericInstanceId::Invalid =
GenericInstanceId(InvalidIndex);
// The ID of an IR within the set of imported IRs, both direct and indirect.
struct ImportIRId : public IdBase, public Printable<ImportIRId> {
using ValueType = ImportIR;
+1
View File
@@ -63,6 +63,7 @@ TEST(SemIRTest, YAML) {
Pair("functions", Yaml::Mapping(SizeIs(1))),
Pair("classes", Yaml::Mapping(SizeIs(0))),
Pair("generics", Yaml::Mapping(SizeIs(0))),
Pair("generic_instances", Yaml::Mapping(SizeIs(0))),
Pair("types", Yaml::Mapping(Each(type_builtin))),
Pair("type_blocks", Yaml::Mapping(SizeIs(Ge(1)))),
Pair("insts",