Add 'let' handling for ImportRefUsed. (#3700)

- File::StringifyTypeExpr now has a case that hits the ImportRefUsed
TODO, so implementing that. I think the `static` approach will be
helpful in ensuring there aren't access bugs, particularly when future
support is added.

- `let` wasn't adding to exports because it doesn't use
`decl_name_stack` the way `var` does. This now adds to exports, but we
might want to unify logic for issues such as this.

- BuildImportRefUsedValueRepr is now called for another inst kind, and
it seemed like calling back to BuildValueRepr was the best way to
resolve this. I don't *think* that's going to cause recursion.
This commit is contained in:
Jon Ross-Perkins
2024-02-13 00:53:13 +00:00
committed by GitHub
parent 99bc9734d9
commit f76bc733f4
9 changed files with 272 additions and 101 deletions
+14 -12
View File
@@ -712,17 +712,9 @@ class TypeCompleter {
return value_rep;
};
auto BuildImportRefUsedValueRepr(SemIR::TypeId type_id,
SemIR::ImportRefUsed import_ref) const
-> SemIR::ValueRepr {
CARBON_CHECK(import_ref.inst_id.is_builtin())
<< "TODO: Handle non-builtin ImportRefUsed cases, such as functions, "
"classes, and interfaces";
const auto& import_ir = context_.import_irs().Get(import_ref.ir_id);
auto import_inst = import_ir->insts().Get(import_ref.inst_id);
switch (import_inst.As<SemIR::Builtin>().builtin_kind) {
auto BuildBuiltinValueRepr(SemIR::TypeId type_id,
SemIR::Builtin builtin) const -> SemIR::ValueRepr {
switch (builtin.builtin_kind) {
case SemIR::BuiltinKind::TypeType:
case SemIR::BuiltinKind::Error:
case SemIR::BuiltinKind::Invalid:
@@ -743,6 +735,16 @@ class TypeCompleter {
llvm_unreachable("All builtin kinds were handled above");
}
auto BuildImportRefUsedValueRepr(SemIR::TypeId type_id,
SemIR::ImportRefUsed import_ref) const
-> SemIR::ValueRepr {
const auto& import_ir = context_.import_irs().Get(import_ref.ir_id);
auto import_inst = import_ir->insts().Get(import_ref.inst_id);
CARBON_CHECK(import_inst.kind() != SemIR::InstKind::ImportRefUsed)
<< "If ImportRefUsed can point at another, this would be recursive.";
return BuildValueRepr(type_id, import_inst);
}
auto BuildStructOrTupleValueRepr(std::size_t num_elements,
SemIR::TypeId elementwise_rep,
bool same_as_object_rep) const
@@ -928,7 +930,7 @@ class TypeCompleter {
return MakeEmptyValueRepr();
case SemIR::Builtin::Kind:
CARBON_FATAL() << "Builtins should be named as ImportRefUsed";
return BuildBuiltinValueRepr(type_id, inst.As<SemIR::Builtin>());
case SemIR::BindSymbolicName::Kind:
case SemIR::PointerType::Kind:
+3
View File
@@ -71,6 +71,9 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool {
// Add the name of the binding to the current scope.
auto name_id = context.bind_names().Get(bind_name.bind_name_id).name_id;
context.AddNameToLookup(name_id, pattern_id);
if (context.scope_stack().PeekNameScopeId() == SemIR::NameScopeId::Package) {
context.AddExport(pattern_id);
}
return true;
}
+3 -2
View File
@@ -21,9 +21,10 @@ static auto GetImportName(const SemIR::File& import_sem_ir,
SemIR::Inst import_inst)
-> std::pair<SemIR::NameId, SemIR::NameScopeId> {
switch (import_inst.kind()) {
case SemIR::InstKind::BindName: {
case SemIR::InstKind::BindName:
case SemIR::InstKind::BindSymbolicName: {
const auto& bind_name = import_sem_ir.bind_names().Get(
import_inst.As<SemIR::BindName>().bind_name_id);
import_inst.As<SemIR::AnyBindName>().bind_name_id);
return {bind_name.name_id, bind_name.enclosing_scope_id};
}
+55 -44
View File
@@ -55,6 +55,23 @@ class ImportRefResolver {
return constant_id;
}
// Wraps constant evaluation with logic to handle types.
auto ResolveType(SemIR::TypeId import_type_id) -> SemIR::TypeId {
if (!import_type_id.is_valid()) {
return import_type_id;
}
auto import_type_inst_id = import_ir_.types().GetInstId(import_type_id);
CARBON_CHECK(import_type_inst_id.is_valid());
if (import_type_inst_id.is_builtin()) {
// Builtins don't require constant resolution; we can use them directly.
return context_.GetBuiltinType(import_type_inst_id.builtin_kind());
} else {
return context_.GetTypeIdForTypeConstant(Resolve(import_type_inst_id));
}
}
private:
// Returns the ConstantId for an instruction, or adds it to the stack and
// returns Invalid if the ConstantId is not ready.
@@ -104,6 +121,21 @@ class ImportRefResolver {
case SemIR::InstKind::TupleType:
return TryResolveTypedInst(inst.As<SemIR::TupleType>());
case SemIR::InstKind::BindName:
case SemIR::InstKind::BindSymbolicName:
// Can use TryEvalInst because the resulting constant doesn't really use
// `inst`.
return TryEvalInst(context_, inst_id, inst);
case SemIR::InstKind::ClassDecl:
case SemIR::InstKind::InterfaceDecl:
// TODO: Not implemented.
return SemIR::ConstantId::Error;
case SemIR::InstKind::FunctionDecl:
// TODO: Allowed to work for testing, but not really implemented.
return SemIR::ConstantId::NotConstant;
default:
context_.TODO(
Parse::NodeId::Invalid,
@@ -219,57 +251,36 @@ class ImportRefResolver {
auto TryResolveImportRefUnused(Context& context, SemIR::InstId inst_id)
-> void {
auto inst = context.insts().Get(inst_id);
auto unused_inst = inst.TryAs<SemIR::ImportRefUnused>();
if (!unused_inst) {
auto import_ref = inst.TryAs<SemIR::ImportRefUnused>();
if (!import_ref) {
return;
}
const SemIR::File& import_ir = *context.import_irs().Get(unused_inst->ir_id);
auto import_inst = import_ir.insts().Get(unused_inst->inst_id);
const SemIR::File& import_ir = *context.import_irs().Get(import_ref->ir_id);
auto& import_ir_constant_values =
context.import_ir_constant_values()[import_ref->ir_id.index];
auto import_inst = import_ir.insts().Get(import_ref->inst_id);
// TODO: Types need to be specifically addressed here to prevent crashes in
// constant evaluation while support is incomplete. Functions are also
// incomplete, but are allowed to fail differently because they aren't a type.
// The partial function support is useful for some namespace validation.
if (import_inst.Is<SemIR::ClassDecl>() ||
import_inst.Is<SemIR::InterfaceDecl>()) {
context.TODO(
Parse::NodeId::Invalid,
llvm::formatv("TryResolveImportRefUnused on {0}", import_inst.kind())
.str());
context.ReplaceInstBeforeConstantUse(
inst_id, {SemIR::ImportRefUsed{SemIR::TypeId::Error, unused_inst->ir_id,
unused_inst->inst_id}});
return;
ImportRefResolver resolver(context, import_ir, import_ir_constant_values);
auto type_id = resolver.ResolveType(import_inst.type_id());
auto constant_id = resolver.Resolve(import_ref->inst_id);
// TODO: Once ClassDecl/InterfaceDecl are supported (no longer return Error),
// remove this.
if (constant_id == SemIR::ConstantId::Error) {
type_id = SemIR::TypeId::Error;
}
// If the type ID isn't a normal value, forward it directly.
if (!import_inst.type_id().is_valid()) {
context.ReplaceInstBeforeConstantUse(
inst_id,
{SemIR::ImportRefUsed{import_inst.type_id(), unused_inst->ir_id,
unused_inst->inst_id}});
return;
}
// Replace the ImportRefUnused instruction with an ImportRefUsed. This doesn't
// use ReplaceInstBeforeConstantUse because it would trigger TryEvalInst, and
// we're instead doing constant evaluation here in order to minimize recursion
// risks.
context.sem_ir().insts().Set(
inst_id,
SemIR::ImportRefUsed{type_id, import_ref->ir_id, import_ref->inst_id});
auto import_type_inst_id = import_ir.types().GetInstId(import_inst.type_id());
CARBON_CHECK(import_type_inst_id.is_valid());
auto type_id = SemIR::TypeId::Invalid;
if (import_type_inst_id.is_builtin()) {
// Builtins don't require constant resolution; we can use them directly.
type_id = context.GetBuiltinType(import_type_inst_id.builtin_kind());
} else {
ImportRefResolver resolver(
context, import_ir,
context.import_ir_constant_values()[unused_inst->ir_id.index]);
type_id =
context.GetTypeIdForTypeConstant(resolver.Resolve(import_type_inst_id));
}
// TODO: Add breadcrumbs for lowering.
context.ReplaceInstBeforeConstantUse(
inst_id, {SemIR::ImportRefUsed{type_id, unused_inst->ir_id,
unused_inst->inst_id}});
// Store the constant for both the ImportRefUsed and imported instruction.
context.constant_values().Set(inst_id, constant_id);
}
} // namespace Carbon::Check
+8 -10
View File
@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveImportRefUnused on ClassDecl`.
// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveImportRefUnused on ClassDecl`.
// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveInst on ClassType`.
// CHECK:STDERR: b.carbon: ERROR: Semantics TODO: `TryResolveInst on ClassType`.
@@ -114,23 +112,23 @@ var d: (ForwardDeclared,) = d_ref;
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.Empty = %import_ref.1, .ForwardDeclared = %import_ref.2, .a_ref = %import_ref.3, .b_ref = %import_ref.4, .c_ref = %import_ref.5, .Run = %Run, .a = %a, .b = %b, .c = %c, .d = %d} [template]
// CHECK:STDOUT: %import_ref.1: <error> = import_ref ir1, inst+1, used
// CHECK:STDOUT: %import_ref.2: <error> = import_ref ir1, inst+4, used
// CHECK:STDOUT: %import_ref.1: <error> = import_ref ir1, inst+1, used [template = <error>]
// CHECK:STDOUT: %import_ref.2: <error> = import_ref ir1, inst+4, used [template = <error>]
// CHECK:STDOUT: %import_ref.3: ref <error> = import_ref ir1, inst+12, used
// CHECK:STDOUT: %import_ref.4: ref <error> = import_ref ir1, inst+20, used
// CHECK:STDOUT: %import_ref.5: ref <error> = import_ref ir1, inst+30, used
// CHECK:STDOUT: %Run: <function> = fn_decl @Run [template]
// CHECK:STDOUT: %Empty.ref: <error> = name_ref Empty, %import_ref.1
// CHECK:STDOUT: %Empty.ref: <error> = name_ref Empty, %import_ref.1 [template = <error>]
// CHECK:STDOUT: %a.var: ref <error> = var a
// CHECK:STDOUT: %a: ref <error> = bind_name a, %a.var
// CHECK:STDOUT: %ForwardDeclared.ref.loc13: <error> = name_ref ForwardDeclared, %import_ref.2
// CHECK:STDOUT: %ForwardDeclared.ref.loc13: <error> = name_ref ForwardDeclared, %import_ref.2 [template = <error>]
// CHECK:STDOUT: %b.var: ref <error> = var b
// CHECK:STDOUT: %b: ref <error> = bind_name b, %b.var
// CHECK:STDOUT: %ForwardDeclared.ref.loc14: <error> = name_ref ForwardDeclared, %import_ref.2
// CHECK:STDOUT: %ForwardDeclared.ref.loc14: <error> = name_ref ForwardDeclared, %import_ref.2 [template = <error>]
// CHECK:STDOUT: %.loc14: type = ptr_type <error> [template = <error>]
// CHECK:STDOUT: %c.var: ref <error> = var c
// CHECK:STDOUT: %c: ref <error> = bind_name c, %c.var
// CHECK:STDOUT: %ForwardDeclared.ref.loc18: <error> = name_ref ForwardDeclared, %import_ref.2
// CHECK:STDOUT: %ForwardDeclared.ref.loc18: <error> = name_ref ForwardDeclared, %import_ref.2 [template = <error>]
// CHECK:STDOUT: %.loc18: <error> = tuple_literal (%ForwardDeclared.ref.loc18)
// CHECK:STDOUT: %d.var: ref <error> = var d
// CHECK:STDOUT: %d: ref <error> = bind_name d, %d.var
@@ -138,12 +136,12 @@ var d: (ForwardDeclared,) = d_ref;
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Run() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Empty.ref: <error> = name_ref Empty, file.%import_ref.1
// CHECK:STDOUT: %Empty.ref: <error> = name_ref Empty, file.%import_ref.1 [template = <error>]
// CHECK:STDOUT: %x.var: ref <error> = var x
// CHECK:STDOUT: %x: ref <error> = bind_name x, %x.var
// CHECK:STDOUT: %.loc7: {} = struct_literal ()
// CHECK:STDOUT: assign %x.var, <error>
// CHECK:STDOUT: %ForwardDeclared.ref: <error> = name_ref ForwardDeclared, file.%import_ref.2
// CHECK:STDOUT: %ForwardDeclared.ref: <error> = name_ref ForwardDeclared, file.%import_ref.2 [template = <error>]
// CHECK:STDOUT: %y.var: ref <error> = var y
// CHECK:STDOUT: %y: ref <error> = bind_name y, %y.var
// CHECK:STDOUT: %.loc8: {} = struct_literal ()
+43
View File
@@ -0,0 +1,43 @@
// 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
//
// AUTOUPDATE
// --- implicit.carbon
package Implicit api;
let T:! type = i32;
// --- implicit.impl.carbon
package Implicit impl;
// TODO: Should this be valid?
// CHECK:STDERR: implicit.impl.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `i32` to `T`.
// CHECK:STDERR: let a: T = 0;
// CHECK:STDERR: ^~~~~~~~~~~~~
let a: T = 0;
// CHECK:STDOUT: --- implicit.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {} [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T, i32 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- implicit.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: i32 = int_literal 0 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.T = %import_ref} [template]
// CHECK:STDOUT: %import_ref: type = import_ref ir1, inst+1, used [symbolic]
// CHECK:STDOUT: %T.ref: type = name_ref T, %import_ref [symbolic = %import_ref]
// CHECK:STDOUT: %.loc8: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: %a: T = bind_name a, <error>
// CHECK:STDOUT: }
// CHECK:STDOUT:
+54
View File
@@ -0,0 +1,54 @@
// 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
//
// AUTOUPDATE
// --- implicit.carbon
package Implicit api;
let T:! type = i32;
// --- implicit.impl.carbon
package Implicit impl;
var a: T*;
var b: T = *a;
// CHECK:STDOUT: --- implicit.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {} [template]
// CHECK:STDOUT: %T: type = bind_symbolic_name T, i32 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- implicit.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: type = ptr_type T [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.T = %import_ref, .a = %a, .b = %b} [template]
// CHECK:STDOUT: %import_ref: type = import_ref ir1, inst+1, used [symbolic]
// CHECK:STDOUT: %T.ref.loc4: type = name_ref T, %import_ref [symbolic = %import_ref]
// CHECK:STDOUT: %.loc4: type = ptr_type T [symbolic = constants.%.1]
// CHECK:STDOUT: %a.var: ref T* = var a
// CHECK:STDOUT: %a: ref T* = bind_name a, %a.var
// CHECK:STDOUT: %T.ref.loc5: type = name_ref T, %import_ref [symbolic = %import_ref]
// CHECK:STDOUT: %b.var: ref T = var b
// CHECK:STDOUT: %b: ref T = bind_name b, %b.var
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %a.ref: ref T* = name_ref a, file.%a
// CHECK:STDOUT: %.loc5_13: T* = bind_value %a.ref
// CHECK:STDOUT: %.loc5_12.1: ref T = deref %.loc5_13
// CHECK:STDOUT: %.loc5_12.2: T = bind_value %.loc5_12.1
// CHECK:STDOUT: assign file.%b.var, %.loc5_12.2
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+39
View File
@@ -0,0 +1,39 @@
// 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
//
// AUTOUPDATE
// --- implicit.carbon
package Implicit api;
let a:! bool = true;
// --- implicit.impl.carbon
package Implicit impl;
let b:! bool = a;
// CHECK:STDOUT: --- implicit.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: bool = bool_literal true [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {} [template]
// CHECK:STDOUT: %.loc4: bool = bool_literal true [template = constants.%.1]
// CHECK:STDOUT: %a: bool = bind_symbolic_name a, %.loc4 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- implicit.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.a = %import_ref} [template]
// CHECK:STDOUT: %import_ref: bool = import_ref ir1, inst+1, used [symbolic]
// CHECK:STDOUT: %a.ref: bool = name_ref a, %import_ref [symbolic = %import_ref]
// CHECK:STDOUT: %b: bool = bind_symbolic_name b, %a.ref [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
+53 -33
View File
@@ -261,25 +261,27 @@ static auto GetTypePrecedence(InstKind kind) -> int {
}
}
auto File::StringifyType(TypeId type_id) const -> std::string {
return StringifyTypeExpr(types().GetInstId(type_id));
}
auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
// Implements File::StringifyTypeExpr. Static to prevent accidental use of
// member functions while traversing IRs.
static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir,
InstId outer_inst_id) {
std::string str;
llvm::raw_string_ostream out(str);
struct Step {
// The instruction's file.
const File& sem_ir;
// The instruction to print.
InstId inst_id;
// The index into inst_id to print. Not used by all types.
int index = 0;
auto Next() const -> Step {
return {.inst_id = inst_id, .index = index + 1};
return {.sem_ir = sem_ir, .inst_id = inst_id, .index = index + 1};
}
};
llvm::SmallVector<Step> steps = {{.inst_id = outer_inst_id}};
llvm::SmallVector<Step> steps = {
Step{.sem_ir = outer_sem_ir, .inst_id = outer_inst_id}};
while (!steps.empty()) {
auto step = steps.pop_back_val();
@@ -294,29 +296,35 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
continue;
}
auto inst = insts().Get(step.inst_id);
const auto& sem_ir = step.sem_ir;
// Helper for instructions with the current sem_ir.
auto push_inst_id = [&](InstId inst_id) {
steps.push_back({.sem_ir = sem_ir, .inst_id = inst_id});
};
auto inst = sem_ir.insts().Get(step.inst_id);
switch (inst.kind()) {
case ArrayType::Kind: {
auto array = inst.As<ArrayType>();
if (step.index == 0) {
out << "[";
steps.push_back(step.Next());
steps.push_back(
{.inst_id = types().GetInstId(array.element_type_id)});
push_inst_id(sem_ir.types().GetInstId(array.element_type_id));
} else if (step.index == 1) {
out << "; " << GetArrayBoundValue(array.bound_id) << "]";
out << "; " << sem_ir.GetArrayBoundValue(array.bound_id) << "]";
}
break;
}
case BindSymbolicName::Kind: {
auto name_id = inst.As<BindSymbolicName>().bind_name_id;
out << names().GetFormatted(bind_names().Get(name_id).name_id);
out << sem_ir.names().GetFormatted(
sem_ir.bind_names().Get(name_id).name_id);
break;
}
case ClassType::Kind: {
auto class_name_id =
classes().Get(inst.As<ClassType>().class_id).name_id;
out << names().GetFormatted(class_name_id);
sem_ir.classes().Get(inst.As<ClassType>().class_id).name_id;
out << sem_ir.names().GetFormatted(class_name_id);
break;
}
case ConstType::Kind: {
@@ -325,44 +333,48 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
// Add parentheses if required.
auto inner_type_inst_id =
types().GetInstId(inst.As<ConstType>().inner_id);
if (GetTypePrecedence(insts().Get(inner_type_inst_id).kind()) <
sem_ir.types().GetInstId(inst.As<ConstType>().inner_id);
if (GetTypePrecedence(sem_ir.insts().Get(inner_type_inst_id).kind()) <
GetTypePrecedence(inst.kind())) {
out << "(";
steps.push_back(step.Next());
}
steps.push_back({.inst_id = inner_type_inst_id});
push_inst_id(inner_type_inst_id);
} else if (step.index == 1) {
out << ")";
}
break;
}
case ImportRefUsed::Kind:
out << "<TODO: ImportRefUsed " << step.inst_id << ">";
case ImportRefUsed::Kind: {
auto import_ref = inst.As<ImportRefUsed>();
steps.push_back({.sem_ir = *sem_ir.import_irs().Get(import_ref.ir_id),
.inst_id = import_ref.inst_id});
break;
}
case InterfaceType::Kind: {
auto interface_name_id =
interfaces().Get(inst.As<InterfaceType>().interface_id).name_id;
out << names().GetFormatted(interface_name_id);
auto interface_name_id = sem_ir.interfaces()
.Get(inst.As<InterfaceType>().interface_id)
.name_id;
out << sem_ir.names().GetFormatted(interface_name_id);
break;
}
case NameRef::Kind: {
out << names().GetFormatted(inst.As<NameRef>().name_id);
out << sem_ir.names().GetFormatted(inst.As<NameRef>().name_id);
break;
}
case PointerType::Kind: {
if (step.index == 0) {
steps.push_back(step.Next());
steps.push_back({.inst_id = types().GetInstId(
inst.As<PointerType>().pointee_id)});
push_inst_id(
sem_ir.types().GetInstId(inst.As<PointerType>().pointee_id));
} else if (step.index == 1) {
out << "*";
}
break;
}
case StructType::Kind: {
auto refs = inst_blocks().Get(inst.As<StructType>().fields_id);
auto refs = sem_ir.inst_blocks().Get(inst.As<StructType>().fields_id);
if (refs.empty()) {
out << "{}";
break;
@@ -376,17 +388,17 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
}
steps.push_back(step.Next());
steps.push_back({.inst_id = refs[step.index]});
push_inst_id(refs[step.index]);
break;
}
case StructTypeField::Kind: {
auto field = inst.As<StructTypeField>();
out << "." << names().GetFormatted(field.name_id) << ": ";
steps.push_back({.inst_id = types().GetInstId(field.field_type_id)});
out << "." << sem_ir.names().GetFormatted(field.name_id) << ": ";
push_inst_id(sem_ir.types().GetInstId(field.field_type_id));
break;
}
case TupleType::Kind: {
auto refs = type_blocks().Get(inst.As<TupleType>().elements_id);
auto refs = sem_ir.type_blocks().Get(inst.As<TupleType>().elements_id);
if (refs.empty()) {
out << "()";
break;
@@ -404,15 +416,15 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
break;
}
steps.push_back(step.Next());
steps.push_back({.inst_id = types().GetInstId(refs[step.index])});
push_inst_id(sem_ir.types().GetInstId(refs[step.index]));
break;
}
case UnboundElementType::Kind: {
if (step.index == 0) {
out << "<unbound element of class ";
steps.push_back(step.Next());
steps.push_back({.inst_id = types().GetInstId(
inst.As<UnboundElementType>().class_type_id)});
push_inst_id(sem_ir.types().GetInstId(
inst.As<UnboundElementType>().class_type_id));
} else {
out << ">";
}
@@ -480,6 +492,14 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
return str;
}
auto File::StringifyType(TypeId type_id) const -> std::string {
return StringifyTypeExprImpl(*this, types().GetInstId(type_id));
}
auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string {
return StringifyTypeExprImpl(*this, outer_inst_id);
}
auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
const File* ir = &file;