mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 07:11:04 +01:00
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:
+14
-12
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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
@@ -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 ()
|
||||
|
||||
@@ -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:
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user