mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:38:28 +00:00
When building an expression referencing the result of name lookup, if the lookup result is non-constant then check we can access it from the current scope. * If it is a global, then use a new `GlobalRef` instruction to get the value of that global. In lowering, create global variables to hold the values of global `let` bindings so that `GlobalRef` can access those values. * If it is a local, walk the lexical scopes between it and the point of use and ensure it is usable through from all of them. Error if there's an intervening declarative scope such as a class, add a TODO to handle captures for intervening functions, and allow `typeof(...)` to name things it can't read the value of since it's unevaluated. Global `let`s (which includes `let`s at class scope) now also have their initializers collected into the global inits block. If the initializer is non-constant, the wrapper binding also lives in the global inits block; otherwise it continues to live in the file block and the variable emitted for it is a global constant. Assisted-by: Gemini and Claude via Antigravity
1291 lines
46 KiB
C++
1291 lines
46 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/stringify.h"
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include "clang/AST/Type.h"
|
|
#include "common/concepts.h"
|
|
#include "common/raw_string_ostream.h"
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/sem_ir/declared_facet_type.h"
|
|
#include "toolchain/sem_ir/entity_with_params_base.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/inst_kind.h"
|
|
#include "toolchain/sem_ir/singleton_insts.h"
|
|
#include "toolchain/sem_ir/specific_interface.h"
|
|
#include "toolchain/sem_ir/struct_type_field.h"
|
|
#include "toolchain/sem_ir/sugared_type.h"
|
|
#include "toolchain/sem_ir/type_info.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::SemIR {
|
|
|
|
// Precedence levels for the syntax produced when stringifying an instruction.
|
|
// Higher numbers correspond to higher precedence. An operand needs to be
|
|
// parenthesized if its precedence is lower than that required by the enclosing
|
|
// syntax.
|
|
//
|
|
// Carbon's precedence is a partial order, not a total order, but the only
|
|
// enclosing syntax that we currently check for is prefix and postfix operators,
|
|
// which have higher precedence than all infix operators, so a total order
|
|
// suffices for now.
|
|
enum class Precedence : int8_t {
|
|
// `A where ...`
|
|
Where = -5,
|
|
// `A & B`
|
|
BitwiseAnd = -4,
|
|
// `A as B`
|
|
As = -3,
|
|
// `T*`
|
|
PostfixStar = -2,
|
|
// `const T`, `partial T`
|
|
PrefixType = -1,
|
|
// Names, literals, calls, member access, and anything parenthesized or
|
|
// otherwise bracketed.
|
|
Primary = 0,
|
|
};
|
|
|
|
// Returns the precedence of the syntax that stringifying `inst_id` produces.
|
|
static auto GetPrecedence(const File& sem_ir, InstId inst_id) -> Precedence {
|
|
while (inst_id.has_value()) {
|
|
auto inst = sem_ir.insts().Get(inst_id);
|
|
switch (inst.kind()) {
|
|
case Call::Kind:
|
|
case TypeOfInst::Kind: {
|
|
// These print their constant value instead, if it's different.
|
|
auto const_inst_id =
|
|
sem_ir.constant_values().GetConstantInstId(inst_id);
|
|
if (!const_inst_id.has_value() || const_inst_id == inst_id) {
|
|
return Precedence::Primary;
|
|
}
|
|
inst_id = const_inst_id;
|
|
break;
|
|
}
|
|
case FacetAccessType::Kind: {
|
|
inst_id = inst.As<FacetAccessType>().facet_value_inst_id;
|
|
break;
|
|
}
|
|
case FacetType::Kind: {
|
|
const auto& info = sem_ir.declared_facet_types().Get(
|
|
inst.As<FacetType>().declared_facet_type_id);
|
|
if (info.other_requirements || !info.rewrite_constraints.empty() ||
|
|
!info.self_impls_constraints.empty() ||
|
|
!info.self_impls_named_constraints.empty() ||
|
|
!info.type_impls_interfaces.empty() ||
|
|
!info.type_impls_named_constraints.empty()) {
|
|
return Precedence::Where;
|
|
}
|
|
if (info.extend_constraints.size() +
|
|
info.extend_named_constraints.size() >
|
|
1) {
|
|
return Precedence::BitwiseAnd;
|
|
}
|
|
return Precedence::Primary;
|
|
}
|
|
case FacetValue::Kind:
|
|
case LookupImplWitness::Kind:
|
|
return Precedence::As;
|
|
case PointerType::Kind:
|
|
return Precedence::PostfixStar;
|
|
case ConstType::Kind:
|
|
case PartialType::Kind:
|
|
return Precedence::PrefixType;
|
|
default:
|
|
// TODO: Handle other kinds of expressions with precedence.
|
|
return Precedence::Primary;
|
|
}
|
|
}
|
|
return Precedence::Primary;
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Contains the stack of steps for `Stringify`.
|
|
//
|
|
// Note that when pushing items onto the stack, they're printed in the reverse
|
|
// order of when they were pushed. All reference lifetimes must match the
|
|
// lifetime of `Stringify`.
|
|
class StepStack {
|
|
public:
|
|
struct StopQualifiedNames {};
|
|
struct ResumeQualifiedNames {};
|
|
|
|
// An individual step in the stack, which stringifies some component of a type
|
|
// name.
|
|
using Step = std::variant<InstId, llvm::StringRef, NameId, ElementIndex,
|
|
DeclaredFacetTypeId, StopQualifiedNames,
|
|
ResumeQualifiedNames>;
|
|
|
|
// Support `Push` for a qualified name. e.g., `A.B.C`.
|
|
using QualifiedNameItem = std::pair<NameScopeId, NameId>;
|
|
|
|
// Support `Push` for a qualified entity name. e.g., `A.B.C`.
|
|
using EntityNameItem = std::pair<const EntityWithParamsBase&, SpecificId>;
|
|
|
|
// The full set of things which can be pushed, including all members of
|
|
// `Step`.
|
|
using PushItem = std::variant<InstId, llvm::StringRef, NameId, ElementIndex,
|
|
QualifiedNameItem, EntityNameItem, EntityNameId,
|
|
SpecificNamedConstraint, SpecificInterface,
|
|
TypeId, llvm::ListSeparator*>;
|
|
|
|
// Starts a new stack, which always contains the first instruction to
|
|
// stringify.
|
|
explicit StepStack(const File* file) : sem_ir_(file) {}
|
|
|
|
// These push basic entries onto the stack.
|
|
auto PushInstId(InstId inst_id) -> void { steps_.push_back(inst_id); }
|
|
auto PushString(llvm::StringRef string) -> void { steps_.push_back(string); }
|
|
auto PushNameId(NameId name_id) -> void { steps_.push_back(name_id); }
|
|
auto PushElementIndex(ElementIndex element_index) -> void {
|
|
steps_.push_back(element_index);
|
|
}
|
|
auto PushFacetType(DeclaredFacetTypeId declared_facet_type_id) -> void {
|
|
steps_.push_back(declared_facet_type_id);
|
|
}
|
|
auto PushResumeQualfiedNames() -> void {
|
|
steps_.push_back(ResumeQualifiedNames{});
|
|
}
|
|
auto PushStopQualfiedNames() -> void {
|
|
steps_.push_back(StopQualifiedNames{});
|
|
}
|
|
|
|
// Pushes all components of a qualified name (`A.B.C`) onto the stack.
|
|
auto PushQualifiedName(NameScopeId name_scope_id, NameId name_id) -> void {
|
|
if (!name_id.has_value()) {
|
|
PushString("<unnamed>");
|
|
return;
|
|
}
|
|
PushNameId(name_id);
|
|
if (!qualified_names_) {
|
|
return;
|
|
}
|
|
while (name_scope_id.has_value() && name_scope_id != NameScopeId::Package) {
|
|
const auto& name_scope = sem_ir_->name_scopes().Get(name_scope_id);
|
|
// TODO: Decide how to print unnamed scopes.
|
|
if (name_scope.name_id().has_value()) {
|
|
PushString(".");
|
|
// TODO: For a generic scope, pass a SpecificId to this function and
|
|
// include the relevant arguments.
|
|
PushNameId(name_scope.name_id());
|
|
}
|
|
name_scope_id = name_scope.parent_scope_id();
|
|
}
|
|
}
|
|
|
|
// Pushes a specific's entity name onto the stack, such as `A.B(T)`.
|
|
auto PushEntityName(const EntityWithParamsBase& entity,
|
|
SpecificId specific_id) -> void {
|
|
PushSpecificId(entity, specific_id);
|
|
PushQualifiedName(entity.parent_scope_id, entity.name_id);
|
|
}
|
|
|
|
// Pushes a entity name onto the stack, such as `A.B`.
|
|
auto PushEntityNameId(EntityNameId entity_name_id) -> void {
|
|
const auto& entity_name = sem_ir_->entity_names().Get(entity_name_id);
|
|
PushQualifiedName(entity_name.parent_scope_id, entity_name.name_id);
|
|
}
|
|
|
|
// Pushes an instruction by its TypeId.
|
|
auto PushTypeId(TypeId type_id) -> void {
|
|
PushInstId(sem_ir_->types().GetTypeInstId(type_id));
|
|
}
|
|
|
|
// Pushes a specific interface by the interface's entity name.
|
|
auto PushSpecificInterface(SpecificInterface specific_interface) -> void {
|
|
PushEntityName(sem_ir_->interfaces().Get(specific_interface.interface_id),
|
|
specific_interface.specific_id);
|
|
}
|
|
|
|
// Pushes a specific named constraint by the constraint's entity name.
|
|
auto PushSpecificNamedConstraint(
|
|
SpecificNamedConstraint specific_named_constraint) -> void {
|
|
PushEntityName(sem_ir_->named_constraints().Get(
|
|
specific_named_constraint.named_constraint_id),
|
|
specific_named_constraint.specific_id);
|
|
}
|
|
|
|
// Pushes a sequence of items onto the stack. This handles reversal, such that
|
|
// the caller can pass items in print order instead of stack order.
|
|
//
|
|
// Note that with `ListSeparator`, the object's reference isn't stored, but
|
|
// the separator `StringRef` will be. That should be a constant though, so is
|
|
// safe.
|
|
auto PushArray(llvm::ArrayRef<PushItem> items) -> void {
|
|
for (auto item : llvm::reverse(items)) {
|
|
CARBON_KIND_SWITCH(item) {
|
|
case CARBON_KIND(InstId inst_id): {
|
|
PushInstId(inst_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(llvm::StringRef string): {
|
|
PushString(string);
|
|
break;
|
|
}
|
|
case CARBON_KIND(NameId name_id): {
|
|
PushNameId(name_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(ElementIndex element_index): {
|
|
PushElementIndex(element_index);
|
|
break;
|
|
}
|
|
case CARBON_KIND(QualifiedNameItem qualified_name): {
|
|
PushQualifiedName(qualified_name.first, qualified_name.second);
|
|
break;
|
|
}
|
|
case CARBON_KIND(EntityNameItem entity_name): {
|
|
PushEntityName(entity_name.first, entity_name.second);
|
|
break;
|
|
}
|
|
case CARBON_KIND(EntityNameId entity_name_id): {
|
|
PushEntityNameId(entity_name_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(TypeId type_id): {
|
|
PushTypeId(type_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(SpecificInterface specific_interface): {
|
|
PushSpecificInterface(specific_interface);
|
|
break;
|
|
}
|
|
case CARBON_KIND(SpecificNamedConstraint specific_named_constraint): {
|
|
PushSpecificNamedConstraint(specific_named_constraint);
|
|
break;
|
|
}
|
|
case CARBON_KIND(llvm::ListSeparator* sep): {
|
|
PushString(*sep);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wraps `PushArray` without requiring `{}` for arguments.
|
|
template <typename... T>
|
|
auto Push(T... items) -> void {
|
|
PushArray({items...});
|
|
}
|
|
|
|
auto empty() const -> bool { return steps_.empty(); }
|
|
auto Pop() -> Step { return steps_.pop_back_val(); }
|
|
|
|
auto SetQualifiedNames(bool qualified_names) -> void {
|
|
qualified_names_ = qualified_names;
|
|
}
|
|
|
|
private:
|
|
// Handles the generic portion of a specific entity name, such as `(T)` in
|
|
// `A.B(T)`.
|
|
auto PushSpecificId(const EntityWithParamsBase& entity,
|
|
SpecificId specific_id) -> void {
|
|
if (!entity.param_patterns_id.has_value()) {
|
|
return;
|
|
}
|
|
int num_params =
|
|
sem_ir_->inst_blocks().Get(entity.param_patterns_id).size();
|
|
if (!num_params) {
|
|
PushString("()");
|
|
return;
|
|
}
|
|
if (!specific_id.has_value()) {
|
|
// The name of the generic was used within the generic itself.
|
|
// TODO: Should we print the names of the generic parameters in this
|
|
// case?
|
|
return;
|
|
}
|
|
const auto& specific = sem_ir_->specifics().Get(specific_id);
|
|
|
|
auto drop_back_count = 0;
|
|
const auto& generic = sem_ir_->generics().Get(specific.generic_id);
|
|
if (sem_ir_->insts()
|
|
.IsOneOf<InterfaceWithSelfDecl, NamedConstraintWithSelfDecl>(
|
|
generic.decl_id)) {
|
|
// The with-self generic contains an additional `Self` parameter beyond
|
|
// the parameters of the entity, the argument for which we do not include
|
|
// in the display.
|
|
drop_back_count = 1;
|
|
}
|
|
|
|
auto args = sem_ir_->inst_blocks()
|
|
.Get(specific.args_id)
|
|
.drop_back(drop_back_count)
|
|
.take_back(num_params);
|
|
bool last = true;
|
|
for (auto arg : llvm::reverse(args)) {
|
|
PushString(last ? ")" : ", ");
|
|
PushInstId(arg);
|
|
last = false;
|
|
}
|
|
PushString("(");
|
|
}
|
|
|
|
const File* sem_ir_;
|
|
// Remaining steps to take.
|
|
llvm::SmallVector<Step> steps_;
|
|
bool qualified_names_ = true;
|
|
};
|
|
|
|
// Provides `StringifyInst` overloads for each instruction.
|
|
class Stringifier {
|
|
public:
|
|
explicit Stringifier(const File* sem_ir, StepStack* step_stack,
|
|
llvm::raw_ostream* out)
|
|
: sem_ir_(sem_ir), step_stack_(step_stack), out_(out) {}
|
|
|
|
// By default try to print a constant, but otherwise may fail to
|
|
// stringify.
|
|
auto StringifyInstDefault(InstId inst_id, Inst inst) -> void {
|
|
// We don't know how to print this instruction, but it might have a
|
|
// constant value that we can print.
|
|
auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id);
|
|
if (const_inst_id.has_value() && const_inst_id != inst_id) {
|
|
step_stack_->PushInstId(const_inst_id);
|
|
return;
|
|
}
|
|
|
|
// We don't need to handle stringification for instructions that don't
|
|
// show up in errors, but make it clear what's going on so that it's
|
|
// clearer when stringification is needed.
|
|
*out_ << "<cannot stringify " << inst_id << ": " << inst << ">";
|
|
}
|
|
|
|
template <typename InstT>
|
|
auto StringifyInst(InstId inst_id, InstT inst) -> void {
|
|
// This doesn't use requires so that more specific overloads are chosen when
|
|
// provided. Indirect constants can be printed by desugaring.
|
|
static_assert(
|
|
InstT::Kind.is_type() != InstIsType::Always ||
|
|
InstT::Kind.constant_kind() == InstConstantKind::Indirect ||
|
|
std::same_as<InstT, WhereExpr>,
|
|
"Types should have a dedicated overload");
|
|
// TODO: We should have Stringify support for all types where
|
|
// InstT::Kind.constant_kind() is neither Never nor Indirect.
|
|
StringifyInstDefault(inst_id, inst);
|
|
}
|
|
|
|
// Singleton instructions use their IR name as a label.
|
|
template <typename InstT>
|
|
requires(IsSingletonInstKind(InstT::Kind))
|
|
auto StringifyInst(InstId /*inst_id*/, InstT /*inst*/) -> void {
|
|
*out_ << InstT::Kind.ir_name();
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ArrayType inst) -> void {
|
|
*out_ << "<builtin array(";
|
|
step_stack_->Push(inst.element_type_inst_id, ", ", inst.bound_id, ")>");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, AssociatedConstantDecl inst) -> void {
|
|
const auto& assoc_const =
|
|
sem_ir_->associated_constants().Get(inst.assoc_const_id);
|
|
step_stack_->PushQualifiedName(assoc_const.parent_scope_id,
|
|
assoc_const.name_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, AssociatedEntityType inst) -> void {
|
|
*out_ << "<associated entity in ";
|
|
step_stack_->Push(">");
|
|
step_stack_->PushSpecificInterface(inst.GetSpecificInterface());
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, BoolLiteral inst) -> void {
|
|
step_stack_->Push(inst.value.ToBool() ? "true" : "false");
|
|
}
|
|
|
|
template <typename InstT>
|
|
requires(SameAsOneOf<InstT, AliasBinding, SymbolicBinding, ExportDecl>)
|
|
auto StringifyInst(InstId /*inst_id*/, InstT inst) -> void {
|
|
step_stack_->PushEntityNameId(inst.entity_name_id);
|
|
}
|
|
|
|
// Pushes the argument list of a call, including the enclosing parentheses.
|
|
//
|
|
// The arguments of a `Call` are the arguments of the SemIR calling
|
|
// convention: compile-time arguments are absent, because they're instead
|
|
// found in the callee's specific, and the remaining arguments are flattened
|
|
// by pattern matching. In order to print the call as it was written, we walk
|
|
// the parameter patterns of the callee and pick out the argument
|
|
// corresponding to each leaf pattern.
|
|
auto PushCallArgs(llvm::ArrayRef<InstId> param_patterns,
|
|
llvm::ArrayRef<InstId> args,
|
|
llvm::ArrayRef<InstId> specific_args) -> void {
|
|
// The pieces of the argument list that we've not printed yet, in print
|
|
// order. We process these from the back, so that we walk the patterns from
|
|
// right to left, which is the order in which the step stack wants to be
|
|
// given them, and which lets us consume `args` from the back.
|
|
llvm::SmallVector<std::variant<InstId, llvm::StringRef>> worklist;
|
|
worklist.push_back(llvm::StringRef("("));
|
|
llvm::ListSeparator sep;
|
|
for (auto param_id : param_patterns) {
|
|
worklist.push_back(llvm::StringRef(sep));
|
|
worklist.push_back(param_id);
|
|
}
|
|
worklist.push_back(llvm::StringRef(")"));
|
|
|
|
while (!worklist.empty()) {
|
|
auto next = worklist.pop_back_val();
|
|
if (auto* string = std::get_if<llvm::StringRef>(&next)) {
|
|
step_stack_->PushString(*string);
|
|
continue;
|
|
}
|
|
|
|
auto pattern_id = std::get<InstId>(next);
|
|
CARBON_KIND_SWITCH(sem_ir_->insts().Get(pattern_id)) {
|
|
case CARBON_KIND(TuplePattern tuple): {
|
|
auto elements = sem_ir_->inst_blocks().Get(tuple.elements_id);
|
|
worklist.push_back(llvm::StringRef("("));
|
|
llvm::ListSeparator element_sep;
|
|
for (auto element_id : elements) {
|
|
worklist.push_back(llvm::StringRef(element_sep));
|
|
worklist.push_back(element_id);
|
|
}
|
|
// A tuple of one element has a comma to disambiguate from a
|
|
// parenthesized pattern.
|
|
worklist.push_back(
|
|
llvm::StringRef(elements.size() == 1 ? ",)" : ")"));
|
|
break;
|
|
}
|
|
case CARBON_KIND_ANY(AnyVarPattern, var_pattern): {
|
|
worklist.push_back(var_pattern.subpattern_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(DefaultValuePattern default_value): {
|
|
worklist.push_back(default_value.subpattern_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND_ANY(AnyBindingPattern, binding): {
|
|
if (binding.subpattern_id.has_value()) {
|
|
worklist.push_back(binding.subpattern_id);
|
|
break;
|
|
}
|
|
// A compile-time binding's argument is an argument of the specific.
|
|
auto bind_index =
|
|
sem_ir_->entity_names().Get(binding.entity_name_id).bind_index();
|
|
if (bind_index.has_value() &&
|
|
static_cast<size_t>(bind_index.index) < specific_args.size()) {
|
|
step_stack_->PushInstId(specific_args[bind_index.index]);
|
|
} else {
|
|
// We don't know the argument, so name the parameter instead.
|
|
step_stack_->PushEntityNameId(binding.entity_name_id);
|
|
}
|
|
break;
|
|
}
|
|
case CARBON_KIND_ANY(AnyLeafParamPattern, _): {
|
|
// A runtime parameter's argument is the next argument of the call,
|
|
// taken from the back because we're walking right to left.
|
|
if (args.empty()) {
|
|
step_stack_->PushString("<missing argument>");
|
|
} else {
|
|
step_stack_->PushInstId(args.back());
|
|
args = args.drop_back();
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
// We don't know how to find the argument for this pattern, so print
|
|
// the pattern instead.
|
|
step_stack_->PushInstId(pattern_id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId inst_id, Call inst) -> void {
|
|
// If the call has a different constant value, for example because it was
|
|
// evaluated at compile time, print that instead.
|
|
auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id);
|
|
if (const_inst_id.has_value() && const_inst_id != inst_id) {
|
|
step_stack_->PushInstId(const_inst_id);
|
|
return;
|
|
}
|
|
|
|
auto args = sem_ir_->inst_blocks().Get(inst.args_id);
|
|
auto callee = GetCallee(*sem_ir_, inst.callee_id);
|
|
auto* callee_fn = std::get_if<CalleeFunction>(&callee);
|
|
if (!callee_fn) {
|
|
// We don't know the signature of the callee, so print the arguments of
|
|
// the `Call` directly.
|
|
step_stack_->PushString(")");
|
|
llvm::ListSeparator sep;
|
|
for (auto arg : llvm::reverse(args)) {
|
|
step_stack_->Push(arg, &sep);
|
|
}
|
|
step_stack_->Push("(", inst.callee_id);
|
|
return;
|
|
}
|
|
|
|
const auto& function = sem_ir_->functions().Get(callee_fn->function_id);
|
|
auto specific_id = callee_fn->resolved_specific_id.has_value()
|
|
? callee_fn->resolved_specific_id
|
|
: callee_fn->enclosing_specific_id;
|
|
|
|
llvm::ArrayRef<InstId> param_patterns;
|
|
if (function.param_patterns_id.has_value()) {
|
|
param_patterns = sem_ir_->inst_blocks().Get(function.param_patterns_id);
|
|
}
|
|
|
|
llvm::ArrayRef<InstId> specific_args;
|
|
if (specific_id.has_value()) {
|
|
specific_args = sem_ir_->inst_blocks().Get(
|
|
sem_ir_->specifics().Get(specific_id).args_id);
|
|
}
|
|
|
|
// Only the explicit parameters are written as arguments in the call.
|
|
const auto& param_ranges = function.call_param_ranges;
|
|
auto args_begin =
|
|
std::min<size_t>(param_ranges.explicit_begin().index, args.size());
|
|
auto args_end =
|
|
std::min<size_t>(param_ranges.explicit_end().index, args.size());
|
|
args = args.slice(args_begin, args_end - args_begin);
|
|
|
|
// In a method call, `self` is the first explicit parameter, but is written
|
|
// before the name of the function rather than in the argument list.
|
|
auto self_id = InstId::None;
|
|
if (callee_fn->self_id.has_value() && function.self_param_id.has_value() &&
|
|
!param_patterns.empty() && !args.empty()) {
|
|
self_id = args.front();
|
|
args = args.drop_front();
|
|
param_patterns = param_patterns.drop_front();
|
|
}
|
|
|
|
PushCallArgs(param_patterns, args, specific_args);
|
|
|
|
// Print the name of the callee. Note that we avoid stringifying the callee
|
|
// instruction itself when it names a function, because that would print
|
|
// the function's parameter list, and we're printing the call's arguments
|
|
// instead.
|
|
if (auto specific_impl_fn =
|
|
sem_ir_->insts().TryGetAs<SpecificImplFunction>(inst.callee_id)) {
|
|
// The callee of a `specific_impl_function` is an `impl_witness_access`,
|
|
// which names both the interface function and the `Self` type.
|
|
step_stack_->PushInstId(specific_impl_fn->callee_id);
|
|
} else {
|
|
step_stack_->PushQualifiedName(function.parent_scope_id,
|
|
function.name_id);
|
|
}
|
|
|
|
if (self_id.has_value()) {
|
|
// TODO: Omit the parentheses when they're not needed.
|
|
step_stack_->Push("(", self_id, ").");
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ClassType inst) -> void {
|
|
const auto& class_info = sem_ir_->classes().Get(inst.class_id);
|
|
if (auto type_info = RecognizedTypeInfo::ForType(*sem_ir_, inst);
|
|
type_info.is_valid()) {
|
|
if (type_info.kind == RecognizedTypeInfo::Array) {
|
|
auto args = sem_ir_->inst_blocks().Get(type_info.args_id);
|
|
if (args.size() == 2) {
|
|
*out_ << "array(";
|
|
step_stack_->Push(args[0], ", ", args[1], ")");
|
|
return;
|
|
}
|
|
}
|
|
if (type_info.PrintLiteral(*sem_ir_, *out_)) {
|
|
return;
|
|
}
|
|
}
|
|
step_stack_->PushEntityName(class_info, inst.specific_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ConstType inst) -> void {
|
|
*out_ << "const ";
|
|
|
|
// `const (const T)` is the same type as `const T`, so don't print a chain
|
|
// of `const`s.
|
|
// TODO: Do this generically for all type qualifiers, and don't repeat
|
|
// qualifiers in types like `const (partial (const T))`.
|
|
auto inner_id = inst.inner_id;
|
|
while (auto inner_const = sem_ir_->insts().TryGetAs<ConstType>(inner_id)) {
|
|
inner_id = inner_const->inner_id;
|
|
}
|
|
|
|
// Add parentheses if required.
|
|
if (GetPrecedence(*sem_ir_, inner_id) < Precedence::PrefixType) {
|
|
*out_ << "(";
|
|
// Note the `inner_id` ends up here.
|
|
step_stack_->PushString(")");
|
|
}
|
|
|
|
step_stack_->PushInstId(inner_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, CppTemplateNameType inst) -> void {
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(inst.name_id, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, CustomLayoutType inst) -> void {
|
|
auto layout = sem_ir_->custom_layouts().Get(inst.layout_id);
|
|
*out_ << "<size " << layout[CustomLayoutId::SizeIndex] << ", align "
|
|
<< layout[CustomLayoutId::AlignIndex] << ">";
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FacetAccessType inst) -> void {
|
|
// Given `T: I`, print `T as type` as simply `T`.
|
|
step_stack_->PushInstId(inst.facet_value_inst_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FacetType inst) -> void {
|
|
step_stack_->PushFacetType(inst.declared_facet_type_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FacetValue inst) -> void {
|
|
// No need to output the witness.
|
|
step_stack_->Push(inst.type_inst_id, " as ", inst.type_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FloatType inst) -> void {
|
|
*out_ << "<builtin ";
|
|
step_stack_->PushString(">");
|
|
if (auto width_value =
|
|
sem_ir_->insts().TryGetAs<IntValue>(inst.bit_width_id)) {
|
|
*out_ << "f";
|
|
sem_ir_->ints().Get(width_value->int_id).print(*out_, /*isSigned=*/false);
|
|
} else {
|
|
*out_ << "Core.Float(";
|
|
step_stack_->Push(inst.bit_width_id, ")");
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, CppOverloadSetType inst) -> void {
|
|
const auto& overload_set =
|
|
sem_ir_->cpp_overload_sets().Get(inst.overload_set_id);
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(StepStack::QualifiedNameItem{overload_set.parent_scope_id,
|
|
overload_set.name_id},
|
|
">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, CppFunctionPointerType inst) -> void {
|
|
clang::QualType clang_type(sem_ir_->clang_function_pointer_types()
|
|
.Get(inst.clang_type_id)
|
|
.clang_type,
|
|
0);
|
|
*out_ << "<C++ type " << clang_type.getAsString() << ">";
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FunctionDecl inst) -> void {
|
|
const auto& fn = sem_ir_->functions().Get(inst.function_id);
|
|
step_stack_->PushQualifiedName(fn.parent_scope_id, fn.name_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FunctionType inst) -> void {
|
|
const auto& fn = sem_ir_->functions().Get(inst.function_id);
|
|
if (!fn.name_id.has_value()) {
|
|
*out_ << "<type of function expression>";
|
|
return;
|
|
}
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(
|
|
StepStack::QualifiedNameItem{fn.parent_scope_id, fn.name_id}, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, FunctionTypeWithSelfType inst)
|
|
-> void {
|
|
StepStack::PushItem fn_name = InstId::None;
|
|
if (auto fn_inst = sem_ir_->insts().TryGetAs<FunctionType>(
|
|
inst.interface_function_type_id)) {
|
|
const auto& fn = sem_ir_->functions().Get(fn_inst->function_id);
|
|
fn_name = StepStack::QualifiedNameItem(fn.parent_scope_id, fn.name_id);
|
|
} else {
|
|
fn_name = inst.interface_function_type_id;
|
|
}
|
|
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(fn_name, " in ", inst.self_id, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ParameterizedClassType inst) -> void {
|
|
const auto& class_info = sem_ir_->classes().Get(inst.class_id);
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(StepStack::QualifiedNameItem{class_info.parent_scope_id,
|
|
class_info.name_id},
|
|
">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, GenericInterfaceType inst) -> void {
|
|
const auto& interface = sem_ir_->interfaces().Get(inst.interface_id);
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(StepStack::QualifiedNameItem{interface.parent_scope_id,
|
|
interface.name_id},
|
|
">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, GenericNamedConstraintType inst)
|
|
-> void {
|
|
const auto& constraint =
|
|
sem_ir_->named_constraints().Get(inst.named_constraint_id);
|
|
*out_ << "<type of ";
|
|
step_stack_->Push(StepStack::QualifiedNameItem{constraint.parent_scope_id,
|
|
constraint.name_id},
|
|
">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, SpliceInst inst) -> void {
|
|
*out_ << "<splice of ";
|
|
step_stack_->Push(inst.inst_id, ">");
|
|
}
|
|
|
|
// Determine the specific interface that an impl witness instruction provides
|
|
// an implementation of.
|
|
// TODO: Should we track this in the type?
|
|
auto TryGetSpecificInterfaceForImplWitness(InstId impl_witness_id)
|
|
-> std::optional<SpecificInterface> {
|
|
if (auto lookup =
|
|
sem_ir_->insts().TryGetAs<LookupImplWitness>(impl_witness_id)) {
|
|
return sem_ir_->specific_interfaces().Get(
|
|
lookup->query_specific_interface_id);
|
|
}
|
|
|
|
// TODO: Handle ImplWitness.
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ImplWitnessAccess inst) -> void {
|
|
auto witness_inst_id =
|
|
sem_ir_->constant_values().GetConstantInstId(inst.witness_id);
|
|
|
|
auto specific_interface = SemIR::SpecificInterface::None;
|
|
if (auto self_witness =
|
|
sem_ir_->insts().TryGetAs<ImplSelfWitness>(witness_inst_id)) {
|
|
specific_interface = sem_ir_->specific_interfaces().Get(
|
|
self_witness->specific_interface_id);
|
|
} else if (auto id_witness = sem_ir_->insts().TryGetAs<IdentifiedWitness>(
|
|
witness_inst_id)) {
|
|
specific_interface = sem_ir_->specific_interfaces().Get(
|
|
id_witness->query_specific_interface_id);
|
|
} else {
|
|
auto lookup = sem_ir_->insts().GetAs<LookupImplWitness>(witness_inst_id);
|
|
specific_interface = sem_ir_->specific_interfaces().Get(
|
|
lookup.query_specific_interface_id);
|
|
}
|
|
|
|
const auto& interface =
|
|
sem_ir_->interfaces().Get(specific_interface.interface_id);
|
|
if (!interface.associated_entities_id.has_value()) {
|
|
step_stack_->Push(".(TODO: element ", inst.index, " in incomplete ",
|
|
witness_inst_id, ")");
|
|
} else {
|
|
auto entities =
|
|
sem_ir_->inst_blocks().Get(interface.associated_entities_id);
|
|
size_t index = inst.index.index;
|
|
CARBON_CHECK(index < entities.size(), "Access out of bounds.");
|
|
auto entity_inst_id = entities[index];
|
|
step_stack_->PushString(")");
|
|
step_stack_->PushResumeQualfiedNames();
|
|
CARBON_KIND_SWITCH(sem_ir_->insts().Get(entity_inst_id)) {
|
|
case CARBON_KIND(AssociatedConstantDecl associated_const): {
|
|
step_stack_->PushNameId(sem_ir_->associated_constants()
|
|
.Get(associated_const.assoc_const_id)
|
|
.name_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(FunctionDecl function_decl): {
|
|
const auto& function =
|
|
sem_ir_->functions().Get(function_decl.function_id);
|
|
step_stack_->PushNameId(function.name_id);
|
|
break;
|
|
}
|
|
default: {
|
|
step_stack_->PushInstId(entity_inst_id);
|
|
break;
|
|
}
|
|
}
|
|
// Don't qualify names after the `.` operator, until the closing `)`.
|
|
step_stack_->PushStopQualfiedNames();
|
|
step_stack_->Push(".");
|
|
step_stack_->Push(
|
|
StepStack::EntityNameItem{interface, specific_interface.specific_id});
|
|
step_stack_->Push(".(");
|
|
}
|
|
|
|
if (auto self_witness =
|
|
sem_ir_->insts().TryGetAs<ImplSelfWitness>(witness_inst_id)) {
|
|
bool is_period_self = false;
|
|
if (auto sym_name = sem_ir_->insts().TryGetAs<SymbolicBinding>(
|
|
self_witness->period_self)) {
|
|
auto name_id =
|
|
sem_ir_->entity_names().Get(sym_name->entity_name_id).name_id;
|
|
is_period_self = (name_id == NameId::PeriodSelf);
|
|
}
|
|
if (!is_period_self) {
|
|
step_stack_->PushInstId(self_witness->period_self);
|
|
}
|
|
} else if (auto lookup = sem_ir_->insts().TryGetAs<IdentifiedWitness>(
|
|
witness_inst_id)) {
|
|
bool is_period_self = false;
|
|
if (auto sym_name = sem_ir_->insts().TryGetAs<SymbolicBinding>(
|
|
lookup->query_self_inst_id)) {
|
|
auto name_id =
|
|
sem_ir_->entity_names().Get(sym_name->entity_name_id).name_id;
|
|
is_period_self = (name_id == NameId::PeriodSelf);
|
|
}
|
|
if (!is_period_self) {
|
|
step_stack_->PushInstId(lookup->query_self_inst_id);
|
|
}
|
|
} else if (auto lookup = sem_ir_->insts().TryGetAs<LookupImplWitness>(
|
|
witness_inst_id)) {
|
|
bool is_period_self = false;
|
|
if (auto sym_name = sem_ir_->insts().TryGetAs<SymbolicBinding>(
|
|
lookup->query_self_inst_id)) {
|
|
auto name_id =
|
|
sem_ir_->entity_names().Get(sym_name->entity_name_id).name_id;
|
|
is_period_self = (name_id == NameId::PeriodSelf);
|
|
}
|
|
if (!is_period_self) {
|
|
step_stack_->PushInstId(lookup->query_self_inst_id);
|
|
}
|
|
} else {
|
|
// TODO: Omit parens if not needed for precedence.
|
|
step_stack_->Push("(", witness_inst_id, ")");
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, ImportRefUnloaded inst) -> void {
|
|
if (inst.entity_name_id.has_value()) {
|
|
step_stack_->PushEntityNameId(inst.entity_name_id);
|
|
} else {
|
|
*out_ << "<import ref unloaded invalid entity name>";
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, IntType inst) -> void {
|
|
*out_ << "<builtin ";
|
|
step_stack_->PushString(">");
|
|
if (auto width_value =
|
|
sem_ir_->insts().TryGetAs<IntValue>(inst.bit_width_id)) {
|
|
*out_ << (inst.int_kind.is_signed() ? "i" : "u");
|
|
sem_ir_->ints().Get(width_value->int_id).print(*out_, /*isSigned=*/false);
|
|
} else {
|
|
*out_ << (inst.int_kind.is_signed() ? "Int(" : "UInt(");
|
|
step_stack_->Push(inst.bit_width_id, ")");
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, IntValue inst) -> void {
|
|
sem_ir_->ints().Get(inst.int_id).print(*out_, /*isSigned=*/true);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, GlobalRef inst) -> void {
|
|
step_stack_->PushInstId(inst.inst_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, IdentifiedWitness inst) -> void {
|
|
step_stack_->Push(
|
|
inst.query_self_inst_id, " as ",
|
|
sem_ir_->specific_interfaces().Get(inst.query_specific_interface_id));
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, LookupImplWitness inst) -> void {
|
|
step_stack_->Push(
|
|
inst.query_self_inst_id, " as ",
|
|
sem_ir_->specific_interfaces().Get(inst.query_specific_interface_id));
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, MaybeUnformedType inst) -> void {
|
|
step_stack_->Push("<builtin MaybeUnformed(", inst.inner_id, ")>");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, NameRef inst) -> void {
|
|
*out_ << sem_ir_->names().GetFormatted(inst.name_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, Namespace inst) -> void {
|
|
const auto& name_scope = sem_ir_->name_scopes().Get(inst.name_scope_id);
|
|
step_stack_->PushQualifiedName(name_scope.parent_scope_id(),
|
|
name_scope.name_id());
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, PartialType inst) -> void {
|
|
*out_ << "partial ";
|
|
|
|
step_stack_->PushInstId(inst.inner_id);
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, PatternType inst) -> void {
|
|
*out_ << "<pattern for ";
|
|
step_stack_->Push(inst.scrutinee_type_inst_id, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, PointerType inst) -> void {
|
|
step_stack_->Push(inst.pointee_id, "*");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, SpecificFunction inst) -> void {
|
|
auto callee = GetCallee(*sem_ir_, inst.callee_id);
|
|
if (auto* fn = std::get_if<CalleeFunction>(&callee)) {
|
|
step_stack_->PushEntityName(sem_ir_->functions().Get(fn->function_id),
|
|
inst.specific_id);
|
|
return;
|
|
}
|
|
step_stack_->PushString("<invalid specific function>");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, SpecificImplFunction inst) -> void {
|
|
auto callee = GetCallee(*sem_ir_, inst.callee_id);
|
|
if (auto* fn = std::get_if<CalleeFunction>(&callee)) {
|
|
// TODO: The specific_id here is for the interface member, but the
|
|
// entity we're passing is the impl member. This might result in
|
|
// strange output once we render specific arguments properly.
|
|
step_stack_->PushEntityName(sem_ir_->functions().Get(fn->function_id),
|
|
inst.specific_id);
|
|
return;
|
|
}
|
|
step_stack_->PushString("<invalid specific function>");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, StructType inst) -> void {
|
|
auto fields = sem_ir_->struct_type_fields().Get(inst.fields_id);
|
|
if (fields.empty()) {
|
|
*out_ << "{}";
|
|
return;
|
|
}
|
|
*out_ << "{";
|
|
step_stack_->PushString("}");
|
|
llvm::ListSeparator sep;
|
|
for (auto field : llvm::reverse(fields)) {
|
|
step_stack_->Push(".", field.name_id, ": ", field.type_inst_id, &sep);
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, StructValue inst) -> void {
|
|
auto field_values = sem_ir_->inst_blocks().Get(inst.elements_id);
|
|
if (field_values.empty()) {
|
|
*out_ << "{}";
|
|
return;
|
|
}
|
|
auto struct_type = sem_ir_->types().GetAs<StructType>(
|
|
sem_ir_->types().GetObjectRepr(inst.type_id));
|
|
auto fields = sem_ir_->struct_type_fields().Get(struct_type.fields_id);
|
|
if (fields.size() != field_values.size()) {
|
|
*out_ << "{<struct value type length mismatch>}";
|
|
return;
|
|
}
|
|
*out_ << "{";
|
|
step_stack_->PushString("}");
|
|
llvm::ListSeparator sep;
|
|
for (auto [field, value_inst_id] :
|
|
llvm::reverse(llvm::zip_equal(fields, field_values))) {
|
|
step_stack_->Push(".", field.name_id, " = ", value_inst_id, &sep);
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, TupleType inst) -> void {
|
|
auto refs = sem_ir_->inst_blocks().Get(inst.type_elements_id);
|
|
if (refs.empty()) {
|
|
*out_ << "()";
|
|
return;
|
|
}
|
|
*out_ << "(";
|
|
step_stack_->PushString(")");
|
|
// A tuple of one element has a comma to disambiguate from an
|
|
// expression.
|
|
if (refs.size() == 1) {
|
|
step_stack_->PushString(",");
|
|
}
|
|
llvm::ListSeparator sep;
|
|
for (auto ref : llvm::reverse(refs)) {
|
|
step_stack_->Push(ref, &sep);
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, TupleValue inst) -> void {
|
|
auto refs = sem_ir_->inst_blocks().Get(inst.elements_id);
|
|
if (refs.empty()) {
|
|
*out_ << "()";
|
|
return;
|
|
}
|
|
*out_ << "(";
|
|
step_stack_->PushString(")");
|
|
// A tuple of one element has a comma to disambiguate from an
|
|
// expression.
|
|
if (refs.size() == 1) {
|
|
step_stack_->PushString(",");
|
|
}
|
|
llvm::ListSeparator sep;
|
|
for (auto ref : llvm::reverse(refs)) {
|
|
step_stack_->Push(ref, &sep);
|
|
}
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, TypeComponentOf inst) -> void {
|
|
*out_ << "<type component of form ";
|
|
step_stack_->Push(inst.form_inst_id, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId inst_id, TypeOfInst /*inst*/) -> void {
|
|
// Print the constant value if we've already computed the inst.
|
|
auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id);
|
|
if (const_inst_id.has_value() && const_inst_id != inst_id) {
|
|
step_stack_->PushInstId(const_inst_id);
|
|
return;
|
|
}
|
|
*out_ << "<dependent type>";
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, UnboundElementType inst) -> void {
|
|
*out_ << "<unbound element of class ";
|
|
step_stack_->Push(inst.class_type_inst_id, ">");
|
|
}
|
|
|
|
auto StringifyInst(InstId /*inst_id*/, VtablePtr /*inst*/) -> void {
|
|
*out_ << "<vtable ptr>";
|
|
}
|
|
|
|
auto StringifyFacetType(DeclaredFacetTypeId declared_facet_type_id) -> void {
|
|
const DeclaredFacetType& declared_facet_type =
|
|
sem_ir_->declared_facet_types().Get(declared_facet_type_id);
|
|
// Output `where` restrictions.
|
|
bool some_where = false;
|
|
if (declared_facet_type.other_requirements) {
|
|
step_stack_->PushString("...");
|
|
some_where = true;
|
|
}
|
|
for (auto rewrite :
|
|
llvm::reverse(declared_facet_type.rewrite_constraints)) {
|
|
if (some_where) {
|
|
step_stack_->PushString(" and");
|
|
}
|
|
step_stack_->Push(" ", rewrite.lhs_id, " = ", rewrite.rhs_id);
|
|
some_where = true;
|
|
}
|
|
if (!declared_facet_type.self_impls_constraints.empty() ||
|
|
!declared_facet_type.self_impls_named_constraints.empty()) {
|
|
if (some_where) {
|
|
step_stack_->PushString(" and");
|
|
}
|
|
llvm::ListSeparator sep(" & ");
|
|
for (auto impls :
|
|
llvm::reverse(declared_facet_type.self_impls_named_constraints)) {
|
|
step_stack_->Push(impls, &sep);
|
|
}
|
|
for (auto impls :
|
|
llvm::reverse(declared_facet_type.self_impls_constraints)) {
|
|
step_stack_->Push(impls, &sep);
|
|
}
|
|
step_stack_->PushString(" .Self impls ");
|
|
some_where = true;
|
|
}
|
|
for (const auto& type_impls :
|
|
llvm::reverse(declared_facet_type.type_impls_interfaces)) {
|
|
if (some_where) {
|
|
step_stack_->PushString(" and");
|
|
}
|
|
step_stack_->Push(" ", type_impls.self_type, " impls ",
|
|
type_impls.specific_interface);
|
|
some_where = true;
|
|
}
|
|
for (const auto& type_impls :
|
|
llvm::reverse(declared_facet_type.type_impls_named_constraints)) {
|
|
if (some_where) {
|
|
step_stack_->PushString(" and");
|
|
}
|
|
step_stack_->Push(" ", type_impls.self_type, " impls ",
|
|
type_impls.specific_named_constraint);
|
|
some_where = true;
|
|
}
|
|
if (some_where) {
|
|
step_stack_->PushString(" where");
|
|
}
|
|
|
|
// Output extend interface and named constraint requirements.
|
|
if (declared_facet_type.extend_constraints.empty() &&
|
|
declared_facet_type.extend_named_constraints.empty()) {
|
|
step_stack_->PushString("type");
|
|
return;
|
|
}
|
|
llvm::ListSeparator sep(" & ");
|
|
for (auto extend :
|
|
llvm::reverse(declared_facet_type.extend_named_constraints)) {
|
|
step_stack_->Push(extend, &sep);
|
|
}
|
|
for (auto extend : llvm::reverse(declared_facet_type.extend_constraints)) {
|
|
step_stack_->Push(extend, &sep);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const File* sem_ir_;
|
|
StepStack* step_stack_;
|
|
llvm::raw_ostream* out_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static auto Stringify(const File& sem_ir, StepStack& step_stack)
|
|
-> std::string {
|
|
RawStringOstream out;
|
|
|
|
Stringifier stringifier(&sem_ir, &step_stack, &out);
|
|
|
|
while (!step_stack.empty()) {
|
|
CARBON_KIND_SWITCH(step_stack.Pop()) {
|
|
case CARBON_KIND(InstId inst_id): {
|
|
if (!inst_id.has_value()) {
|
|
out << "<invalid>";
|
|
break;
|
|
}
|
|
auto untyped_inst = sem_ir.insts().Get(inst_id);
|
|
CARBON_KIND_SWITCH(untyped_inst) {
|
|
#define CARBON_SEM_IR_INST_KIND(InstT) \
|
|
case CARBON_KIND(InstT typed_inst): { \
|
|
stringifier.StringifyInst(inst_id, typed_inst); \
|
|
break; \
|
|
}
|
|
#include "toolchain/sem_ir/inst_kind.def"
|
|
}
|
|
break;
|
|
}
|
|
case CARBON_KIND(llvm::StringRef string): {
|
|
out << string;
|
|
break;
|
|
}
|
|
case CARBON_KIND(NameId name_id): {
|
|
out << sem_ir.names().GetFormatted(name_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(ElementIndex element_index): {
|
|
out << element_index.index;
|
|
break;
|
|
}
|
|
case CARBON_KIND(DeclaredFacetTypeId declared_facet_type_id): {
|
|
stringifier.StringifyFacetType(declared_facet_type_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(StepStack::StopQualifiedNames _): {
|
|
step_stack.SetQualifiedNames(false);
|
|
break;
|
|
}
|
|
case CARBON_KIND(StepStack::ResumeQualifiedNames _): {
|
|
step_stack.SetQualifiedNames(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return out.TakeStr();
|
|
}
|
|
|
|
auto StringifyConstantInst(const File& sem_ir, InstId outer_inst_id)
|
|
-> std::string {
|
|
StepStack step_stack(&sem_ir);
|
|
step_stack.PushInstId(outer_inst_id);
|
|
return Stringify(sem_ir, step_stack);
|
|
}
|
|
|
|
auto StringifyTypeOfInst(const File& sem_ir, InstId inst_id) -> std::string {
|
|
return StringifyConstantInst(sem_ir, GetSugaredTypeOfInst(sem_ir, inst_id));
|
|
}
|
|
|
|
auto StringifySpecific(const File& sem_ir, SpecificId specific_id)
|
|
-> std::string {
|
|
StepStack step_stack(&sem_ir);
|
|
|
|
const auto& specific = sem_ir.specifics().Get(specific_id);
|
|
const auto& generic = sem_ir.generics().Get(specific.generic_id);
|
|
auto decl = sem_ir.insts().Get(generic.decl_id);
|
|
CARBON_KIND_SWITCH(decl) {
|
|
case CARBON_KIND(ClassDecl class_decl): {
|
|
// Print `Core.Int(N)` as `iN` and `Core.Array(T, N)` as `array(T, N)`.
|
|
// TODO: This duplicates work done in StringifyInst for ClassType.
|
|
const auto& class_info = sem_ir.classes().Get(class_decl.class_id);
|
|
if (auto type_info = RecognizedTypeInfo::ForType(
|
|
sem_ir, ClassType{.type_id = TypeType::TypeId,
|
|
.class_id = class_decl.class_id,
|
|
.specific_id = specific_id});
|
|
type_info.is_valid()) {
|
|
if (type_info.kind == RecognizedTypeInfo::Array) {
|
|
auto args = sem_ir.inst_blocks().Get(type_info.args_id);
|
|
if (args.size() == 2) {
|
|
step_stack.Push("array(", args[0], ", ", args[1], ")");
|
|
break;
|
|
}
|
|
}
|
|
RawStringOstream out;
|
|
if (type_info.PrintLiteral(sem_ir, out)) {
|
|
return out.TakeStr();
|
|
}
|
|
}
|
|
step_stack.PushEntityName(class_info, specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(FunctionDecl function_decl): {
|
|
step_stack.PushEntityName(
|
|
sem_ir.functions().Get(function_decl.function_id), specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(ImplDecl impl_decl): {
|
|
step_stack.PushEntityName(sem_ir.impls().Get(impl_decl.impl_id),
|
|
specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(InterfaceDecl interface_decl): {
|
|
step_stack.PushEntityName(
|
|
sem_ir.interfaces().Get(interface_decl.interface_id), specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(InterfaceWithSelfDecl interface_with_self_decl): {
|
|
step_stack.PushEntityName(
|
|
sem_ir.interfaces().Get(interface_with_self_decl.interface_id),
|
|
specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(NamedConstraintDecl constraint_decl): {
|
|
step_stack.PushEntityName(
|
|
sem_ir.named_constraints().Get(constraint_decl.named_constraint_id),
|
|
specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(NamedConstraintWithSelfDecl constraint_with_self_decl): {
|
|
step_stack.PushEntityName(
|
|
sem_ir.named_constraints().Get(
|
|
constraint_with_self_decl.named_constraint_id),
|
|
specific_id);
|
|
break;
|
|
}
|
|
case CARBON_KIND(RequireImplsDecl _): {
|
|
step_stack.Push("require");
|
|
break;
|
|
}
|
|
default: {
|
|
// TODO: Include the specific arguments here.
|
|
step_stack.PushInstId(generic.decl_id);
|
|
break;
|
|
}
|
|
}
|
|
return Stringify(sem_ir, step_stack);
|
|
}
|
|
|
|
auto StringifySpecificInterface(const File& sem_ir,
|
|
SpecificInterface specific_interface)
|
|
-> std::string {
|
|
if (specific_interface.specific_id.has_value()) {
|
|
return StringifySpecific(sem_ir, specific_interface.specific_id);
|
|
} else {
|
|
auto name_id =
|
|
sem_ir.interfaces().Get(specific_interface.interface_id).name_id;
|
|
return sem_ir.names().GetFormatted(name_id).str();
|
|
}
|
|
}
|
|
|
|
auto StringifyDeclaredFacetType(const File& sem_ir,
|
|
DeclaredFacetTypeId declared_facet_type_id)
|
|
-> std::string {
|
|
StepStack step_stack(&sem_ir);
|
|
step_stack.PushFacetType(declared_facet_type_id);
|
|
return Stringify(sem_ir, step_stack);
|
|
}
|
|
|
|
} // namespace Carbon::SemIR
|