Просмотр исходного кода

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.
Richard Smith 1 год назад
Родитель
Сommit
a0d767246f

+ 3 - 0
toolchain/check/context.h

@@ -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 - 0
toolchain/check/testdata/basics/builtin_insts.carbon

@@ -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:     {}

+ 2 - 0
toolchain/check/testdata/basics/no_prelude/multifile_raw_and_textual_ir.carbon

@@ -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}}

+ 2 - 0
toolchain/check/testdata/basics/no_prelude/multifile_raw_ir.carbon

@@ -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}}

+ 1 - 0
toolchain/check/testdata/basics/no_prelude/raw_and_textual_ir.carbon

@@ -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}}

+ 1 - 0
toolchain/check/testdata/basics/no_prelude/raw_ir.carbon

@@ -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 - 0
toolchain/sem_ir/BUILD

@@ -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 - 0
toolchain/sem_ir/file.cpp

@@ -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 - 0
toolchain/sem_ir/file.h

@@ -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 - 0
toolchain/sem_ir/generic.cpp

@@ -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 - 0
toolchain/sem_ir/generic.h

@@ -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 - 0
toolchain/sem_ir/ids.h

@@ -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 - 0
toolchain/sem_ir/yaml_test.cpp

@@ -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",