Repository: carbon-language/carbon-lang
File: toolchain/check/type_structure.cpp (C++)

Code before:
            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
            Push(CloseType());
            for (const auto& field : fields) {
              Push(field.type_id);
            }
          }
          break;
        }
        case CARBON_KIND(SemIR::TypeOfInst type_of): {
          (void)type_of;
          auto const_id = context_.constant_values().Get(inst_id);
          // TODO: TypeOfInst should not be encountered in impl lookup with a
          // template-dependent value, since impl lookup on such values should
          // be deferred to type-checking the specific. So this should become a
          // CARBON_FATAL code path. For now it is possible, though, to reach
          // here and it results in a diagnostic.
          //
          // However, TypeOfInst can be determined to be a concrete value for a
          // template value with a non-template dependent type. This currently
          // does not happen though, the TypeOfInst is always template
          // dependent. If it does in the future then we will want to use the
          // concrete constant value instruction of `inst_id` in the type
          // structure:
          //   if (const_id.is_concrete()) {
          //     PushInstId(context_.constant_values().GetInstId(const_id));
          //   }
          //
          // If TypeOfInst was used for more general metaprogramming then we may
          // need to handle both concrete and perhaps symbolic (non-template)
          // values of TypeOfInst.
          CARBON_CHECK(const_id.is_symbolic());
          auto sym = context_.constant_values().GetSymbolicConstant(const_id);
          CARBON_CHECK(sym.dependence == SemIR::ConstantDependence::Template,
                       "TypeOfInst with non-template symbolic value?");
          context_.TODO(context_.insts().GetLocId(inst_id),
                        "Impl lookup on template-dependent type value");
          break;
        }
        default:
          CARBON_FATAL("Unhandled type instruction {0}", inst_id);
      }
    }
  }

  // A work item to mark the closing paren for an aggregate concrete type.
  struct CloseType {};
  // A work item to mark a symbolic type.
  struct SymbolicType {};
  // A work item to mark a non-type value.
  struct NonTypeValue {};


Review comment (suggestion):
I think we only see canonical constant type values here, and a `TypeOfInst` instruction will only be a canonical constant value if it's a template constant. (If its argument is an `InstValue`, `TypeOfInst` evaluates to that instruction's value; otherwise it itself defines a template constant.)

I think we probably want to treat a `TypeOfInst` as a `?` in the type structure, because it represents an unknown generic type, and should be a worse match than a known concrete constant type.

Code after:
          }
          break;
        }
        case CARBON_KIND(SemIR::StructType struct_type): {
          auto fields =
              context_.struct_type_fields().Get(struct_type.fields_id);
          if (fields.empty()) {
            AppendStructural(TypeStructure::Structural::Concrete);
          } else {
            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
            Push(CloseType());
            for (const auto& field : fields) {
              Push(field.type_id);
            }
          }
          break;
        }
        default:
          CARBON_FATAL("Unhandled type instruction {0}", inst_id);
      }
    }
  }

  // A work item to mark the closing paren for an aggregate concrete type.
  struct CloseType {};
  // A work item to mark a symbolic type.
  struct SymbolicType {};
  // A work item to mark a non-type value.
  struct NonTypeValue {};

  using WorkItem = std::variant<SemIR::TypeId, SymbolicType, NonTypeValue,
                                SemIR::SpecificInterface, CloseType>;

  // Get the TypeId for an instruction that is not a facet value, otherwise
  // return SymbolicType to indicate the instruction is a symbolic facet value.
  //
  // If the instruction is not a type value, the return is TypeId::None.
  //
  // We reuse the `SymbolicType` work item here to give a nice return type.
  auto TryGetInstIdAsTypeId(SemIR::InstId inst_id) const
      -> std::variant<SemIR::TypeId, SymbolicType> {
    if (auto facet_value =
            context_.insts().TryGetAs<SemIR::FacetValue>(inst_id)) {
      inst_id = facet_value->type_inst_id;
    }

    auto type_id_of_inst_id = context_.insts().Get(inst_id).type_id();
    // All instructions of type FacetType are symbolic except for FacetValue:
    // - In non-generic code, values of type FacetType are only created through
    //   conversion to a FacetType (e.g. `Class as Iface`), which produces a
    //   non-symbolic FacetValue.