Files
carbon-lang/toolchain/sem_ir/expr_info.cpp
T
124313269a Represent vtables as a top level SemIR construct (#5472)
The goal was/is to reduce the overhead for vtables in generics - the
previous representation/prior to this patch caused a new vtable to be
created in every specific which isn't generally what we want for Carbon
generics (the whole specific/generic thing is meant to avoid creating
specific versions for things that can be a generic form parameterized by
a specific instead of manifest as a unique entity per specific)

So this moves vtables to a top level object (like functions, classes,
etc). Each dynamic class will have a vtable in this list.

Classes have a `vtable_ptr` instruction in them that points to the
vtable.

The actual generic support hasn't been implemented in this patch, as
I've been struggling with just getting this part of the migration going
& wanted to get it flushed out before adding the additional
complications.

It's possible more laziness when doing cross-file importing would be
suitable - for instance if we only need to reference the vtable from
another file, but don't need to know its individual contents, it may be
beneficial for the functions in the vtable to be import_refs (or to add
another layer of indirection - so it can be a single import_ref
all-or-nothing for the functions in the vtable).

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
2025-06-27 18:45:26 +00:00

287 lines
8.1 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/expr_info.h"
#include "common/check.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
const File* ir = &file;
// The overall expression category if the current instruction is a value
// expression.
ExprCategory value_category = ExprCategory::Value;
while (true) {
auto untyped_inst = ir->insts().Get(inst_id);
CARBON_KIND_SWITCH(untyped_inst) {
case AdaptDecl::Kind:
case AddrPattern::Kind:
case Assign::Kind:
case BaseDecl::Kind:
case BindingPattern::Kind:
case Branch::Kind:
case BranchIf::Kind:
case BranchWithArg::Kind:
case FieldDecl::Kind:
case FunctionDecl::Kind:
case ImplDecl::Kind:
case NameBindingDecl::Kind:
case Namespace::Kind:
case OutParamPattern::Kind:
case RefParamPattern::Kind:
case RequirementEquivalent::Kind:
case RequirementImpls::Kind:
case RequirementRewrite::Kind:
case Return::Kind:
case ReturnSlotPattern::Kind:
case ReturnExpr::Kind:
case SymbolicBindingPattern::Kind:
case TuplePattern::Kind:
case ValueParamPattern::Kind:
case VarPattern::Kind:
return ExprCategory::NotExpr;
case ImportRefUnloaded::Kind:
case ImportRefLoaded::Kind: {
auto import_ir_inst = ir->import_ir_insts().Get(
untyped_inst.As<AnyImportRef>().import_ir_inst_id);
ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
inst_id = import_ir_inst.inst_id();
continue;
}
case CARBON_KIND(AsCompatible inst): {
inst_id = inst.source_id;
continue;
}
case CARBON_KIND(BindAlias inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(ExportDecl inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(NameRef inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(Converted inst): {
inst_id = inst.result_id;
continue;
}
case CARBON_KIND(ImplWitnessAssociatedConstant inst): {
inst_id = inst.inst_id;
continue;
}
case CARBON_KIND(SpecificConstant inst): {
inst_id = inst.inst_id;
continue;
}
case AccessMemberAction::Kind:
case AccessOptionalMemberAction::Kind:
case AddrOf::Kind:
case ArrayType::Kind:
case AssociatedConstantDecl::Kind:
case AssociatedEntity::Kind:
case AssociatedEntityType::Kind:
case AutoType::Kind:
case BindSymbolicName::Kind:
case BindValue::Kind:
case BlockArg::Kind:
case BoolLiteral::Kind:
case BoolType::Kind:
case BoundMethod::Kind:
case BoundMethodType::Kind:
case ClassDecl::Kind:
case ClassType::Kind:
case CompleteTypeWitness::Kind:
case ConstType::Kind:
case ConvertToValueAction::Kind:
case FacetAccessType::Kind:
case FacetType::Kind:
case FacetValue::Kind:
case FloatLiteral::Kind:
case FloatType::Kind:
case FunctionType::Kind:
case FunctionTypeWithSelfType::Kind:
case GenericClassType::Kind:
case GenericInterfaceType::Kind:
case LookupImplWitness::Kind:
case ImplWitness::Kind:
case ImplWitnessAccess::Kind:
case ImplWitnessTable::Kind:
case ImplWitnessTablePlaceholder::Kind:
case ImportCppDecl::Kind:
case ImportDecl::Kind:
case InstType::Kind:
case InstValue::Kind:
case IntLiteralType::Kind:
case IntType::Kind:
case IntValue::Kind:
case InterfaceDecl::Kind:
case LegacyFloatType::Kind:
case NamespaceType::Kind:
case PartialType::Kind:
case PatternType::Kind:
case PointerType::Kind:
case RefineTypeAction::Kind:
case RequireCompleteType::Kind:
case SpecificFunction::Kind:
case SpecificFunctionType::Kind:
case SpecificImplFunction::Kind:
case StringLiteral::Kind:
case StringType::Kind:
case StructType::Kind:
case StructValue::Kind:
case TupleType::Kind:
case TupleValue::Kind:
case TypeOfInst::Kind:
case TypeType::Kind:
case UnaryOperatorNot::Kind:
case UnboundElementType::Kind:
case ValueOfInitializer::Kind:
case ValueParam::Kind:
case VtableType::Kind:
case WhereExpr::Kind:
case WitnessType::Kind:
return value_category;
case ErrorInst::Kind:
return ExprCategory::Error;
case CARBON_KIND(BindName inst): {
// TODO: Don't rely on value_id for expression category, since it may
// not be valid yet. This workaround only works because we don't support
// `var` in function signatures yet.
if (!inst.value_id.has_value()) {
return value_category;
}
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(ArrayIndex inst): {
inst_id = inst.array_id;
continue;
}
case VtablePtr::Kind:
return ExprCategory::EphemeralRef;
case CARBON_KIND(ClassElementAccess inst): {
inst_id = inst.base_id;
// A value of class type is a pointer to an object representation.
// Therefore, if the base is a value, the result is an ephemeral
// reference.
value_category = ExprCategory::EphemeralRef;
continue;
}
case CARBON_KIND(StructAccess inst): {
inst_id = inst.struct_id;
continue;
}
case CARBON_KIND(TupleAccess inst): {
inst_id = inst.tuple_id;
continue;
}
case CARBON_KIND(SpliceBlock inst): {
inst_id = inst.result_id;
continue;
}
case SpliceInst::Kind:
// TODO: Add ExprCategory::Dependent.
return value_category;
case StructLiteral::Kind:
case TupleLiteral::Kind:
return ExprCategory::Mixed;
case ArrayInit::Kind:
case Call::Kind:
case InitializeFrom::Kind:
case ClassInit::Kind:
case StructInit::Kind:
case TupleInit::Kind:
return ExprCategory::Initializing;
case Deref::Kind:
case VarStorage::Kind:
case ReturnSlot::Kind:
return ExprCategory::DurableRef;
case Temporary::Kind:
case TemporaryStorage::Kind:
case ValueAsRef::Kind:
return ExprCategory::EphemeralRef;
case OutParam::Kind:
case RefParam::Kind:
// TODO: Consider introducing a separate category for OutParam:
// unlike other DurableRefs, it permits initialization.
return ExprCategory::DurableRef;
}
}
}
auto FindReturnSlotArgForInitializer(const File& sem_ir, InstId init_id)
-> InstId {
while (true) {
Inst init_untyped = sem_ir.insts().Get(init_id);
CARBON_KIND_SWITCH(init_untyped) {
case CARBON_KIND(AsCompatible init): {
init_id = init.source_id;
continue;
}
case CARBON_KIND(Converted init): {
init_id = init.result_id;
continue;
}
case CARBON_KIND(ArrayInit init): {
return init.dest_id;
}
case CARBON_KIND(ClassInit init): {
return init.dest_id;
}
case CARBON_KIND(StructInit init): {
return init.dest_id;
}
case CARBON_KIND(TupleInit init): {
return init.dest_id;
}
case CARBON_KIND(InitializeFrom init): {
return init.dest_id;
}
case CARBON_KIND(Call call): {
if (!ReturnTypeInfo::ForType(sem_ir, call.type_id).has_return_slot()) {
return InstId::None;
}
if (!call.args_id.has_value()) {
// Argument initialization failed, so we have no return slot.
return InstId::None;
}
return sem_ir.inst_blocks().Get(call.args_id).back();
}
default:
CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
}
}
}
} // namespace Carbon::SemIR