mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 09:58:34 +00:00
When impl lookup results in an `ImplWitness`, either directly or later through `LookupImplWitness` evaluating to an `ImplWitness` inside some specific, it represents a dependency on the eval blocks of the `ImplDecl` that the `ImplWitness` comes from. The `ImplWitness` contains a `SpecificId` of the `ImplDecl`, and the constants in the witness table must be viewed through that `SpecificId`. The constants may depend on the declaration or the definition block of the `ImplDecl`'s specific, so any use of the witness table from `ImplWitness` requires the specific from the `ImplWitness` to be fully resolved: both the decl and definition blocks. The specific declaration block is always resolved immediately when forming a specific id, so there's nothing to do there. And things are simple when the `ImplWitness` returned from impl lookup is concrete. We would simply resolve the specific definition directly in the evaluation of `LookupImplWitness`. But when the `ImplWitness` is symbolic, meaning there's a dependency on generic parameters in its referenced specific, then we have a dependency on resolving the specific definition in the `ImplWitness` for every specific that gets applied to the `ImplWitness`, eventually resulting in a concrete specific. Previously we attempted to do this by representing a dependency edge from the eval block containing the `ImplWitness` instruction to the definition block of the specific inside the `ImplWitness`. We represented this dependency edge with the `RequireSpecificDefinition` instruction. The challenge with the `RequireSpecificDefinition` instruction was ensuring it always landed in the _same eval block_ as the one holding the symbolic `ImplWitness`. Ultimately, we've found a way to do this without the `RequireSpecificDefinition`. The witness table is accessed through `ImplWitnessAccess`, so we resolve the specific definition before each access to the witness table. If the definition is already resolved, this just costs a lookup of the Specific from the value store. We document the other things we tried, which don't work or were too complex, below. In PR #6236, we added the `RequireSpecificDefinition` instruction in the eval of `LookupImplWitness`, but this is too late. If impl lookup resolved to a symbolic `LookupImplWitness`, the `LookupImplWitness` is added some eval block. Later, we form specifics and re-evaluate that eval block, and the `LookupImplWitness` can evaluate to an `ImplWitness`. Adding the `RequireSpecificDefinition` instruction at this point inserts the instruction into whatever generic's eval block happens to be on the top of the generic stack, not the eval block of the generic we are building a specific for. And indeed we can't add instructions to a generic's eval block later like this, as it would be missing from specifics that were already resolved for the generic. This incorrect insertion shows up as a crash in eval at the CHECK introduced but commented out in [eval.cpp in #7981](https://github.com/carbon-language/carbon-lang/blob/56d9bbc40add1e9263f2f9b2555c3a27292d1725/toolchain/check/eval.cpp#L3507-L3512). This is covered by the `require_specific_impl_definition_through_specific_instantiation.carbon` test. We tried moving the insertion of `RequireSpecificDefinition` to be earlier - at the point where the `LookupImplWitness` is constructed, in the `LookupImplWitness()` function, instead of inside evaluation. This seemed like a straightforwardly correct answer, as the eval block that contains the `LookupImplWitness` will then also contain the `RequireSpecificDefinition`. However, this can cause us to lose the `RequireSpecificDefinition` entirely. Because it's a separate instruction, not an operand of `LookupImplWitness`, it does not get treated as a dependency of the witness. When we do an impl lookup inside an `alias`, we build a generic for the alias and then discard it. This discards the eval block containing the `LookupImplWitness`. Then, in an enclosing generic, we refer to the constructed `AliasDecl` instruction, which pulls all its dependent operands into the enclosing eval block. This pulls in the `LookupImplWitness` into the enclosing eval block, but it does not preserve the `RequireSpecificDefinition`. This is covered by the `require_specific_impl_definition_from_lookup_impl_witness_in_class_definition.carbon` test. We tried having `ImplWitness` evaluation resolve the specific definition of the specific `ImplDecl` it refers to. This seems like the simplest approach by far as we should always be working with the correct eval block and nothing can get lost. However it does not work because of the recursive nature of `ImplWitness`. The `ImplWitness` instruction appears in the declaration of an `ImplDecl`, and the `ImplWitness` holds a `SpecificId` for that same `ImplDecl`. When building a specific `ImplDecl`, we evaluate the `ImplWitness` in the `ImplDecl`'s declaration block which then immediately resolves the specific definition before the declaration is finished. This leads to missing instructions in the specific definition. In short, this puts the dependency edge to the `ImplDecl`'s specific inside the `ImplDecl` itself, which is problematic. We tried wrapping `ImplWitness` in another instruction, `ImplWitnessUse`. Inside the `ImplDecl` declaration there is still `ImplWitness`, but then in the definition there is `ImplWitnessUse` and that is what is returned from the evaluation of `LookupImplWitness`. The `ImplWitnessUse` is an `ImplWitness` plus it ensures the specific referred to by the witness has its definition resolved. However this breaks constant comparisons, as we get different witnesses inside an impl versus outside. The first iteration of this PR's solution had us insert the `RequireSpecificDefinition` into the eval block when we sniffed a `LookupImplWitness` instruction, however that missed cases where impl lookup immediately resolved to a symbolic `ImplWitness`, which is why we now sniff for the `ImplWitness` in the eval block. This is covered by the `require_specific_impl_definition_from_symbolic_impl_in_class_definition.carbon` test. We also originally made a mistake of not inserting `RequireSpecificDefinition` when inside the impl definition that the `ImplWitness` refers to. But we can form specifics ImplWitness values through function signatures, and these must add the dependency edge on that specific ImplDecl definition. This is covered by the `require_specific_impl_definition_from_inside_impl_definition.carbon` test. Finally, we did have a working solution. It was to sniff instructions added to the eval block of a generic, and when an `ImplWitness` is added to the eval block (it is implied that it is symbolic), also add a `RequireSpecificDefinition` to that eval block immediately. We also resolved the definition of a specific referred to by `ImplWitness` when `LookupImplWitness` evaluates to `ImplWitness` to cover the concrete witness case. This complicated the construction of the eval block immensely, however. It caused a situation where adding one inst to the eval block actually adds two, which we needed to avoid when importing. And it required inst-kind specific behaviour in the very general construction of an eval block, completely outside of eval itself.
913 lines
38 KiB
C++
913 lines
38 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/check/eval_inst.h"
|
|
|
|
#include <variant>
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/action.h"
|
|
#include "toolchain/check/cpp/constant.h"
|
|
#include "toolchain/check/cpp/import.h"
|
|
#include "toolchain/check/cpp/type_mapping.h"
|
|
#include "toolchain/check/diagnostic_helpers.h"
|
|
#include "toolchain/check/facet_type.h"
|
|
#include "toolchain/check/generic.h"
|
|
#include "toolchain/check/identified_facet_type.h"
|
|
#include "toolchain/check/impl_lookup.h"
|
|
#include "toolchain/check/import_ref.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/period_self.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/check/type_completion.h"
|
|
#include "toolchain/diagnostics/diagnostic.h"
|
|
#include "toolchain/parse/typed_nodes.h"
|
|
#include "toolchain/sem_ir/builtin_function_kind.h"
|
|
#include "toolchain/sem_ir/expr_info.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/pattern.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// Performs an access into an aggregate, retrieving the specified element.
|
|
static auto PerformAggregateAccess(Context& context, SemIR::Inst inst)
|
|
-> ConstantEvalResult {
|
|
auto access_inst = inst.As<SemIR::AnyAggregateAccess>();
|
|
if (auto aggregate = context.insts().TryGetAs<SemIR::AnyAggregateValue>(
|
|
access_inst.aggregate_id)) {
|
|
auto elements = context.inst_blocks().Get(aggregate->elements_id);
|
|
auto index = static_cast<size_t>(access_inst.index.index);
|
|
CARBON_CHECK(index < elements.size(), "Access out of bounds.");
|
|
// `Phase` is not used here. If this element is a concrete constant, then
|
|
// so is the result of indexing, even if the aggregate also contains a
|
|
// symbolic context.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(elements[index]));
|
|
}
|
|
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::ArrayInit inst)
|
|
-> ConstantEvalResult {
|
|
// TODO: Add an `ArrayValue` to represent a constant array object
|
|
// representation instead of using a `TupleValue`.
|
|
return ConstantEvalResult::NewSamePhase(
|
|
SemIR::TupleValue{.type_id = inst.type_id, .elements_id = inst.inits_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::ArrayType inst) -> ConstantEvalResult {
|
|
auto orig_inst = context.insts().GetAs<SemIR::ArrayType>(inst_id);
|
|
auto error_loc =
|
|
context.insts().GetCanonicalLocId(orig_inst.bound_id).has_value()
|
|
? orig_inst.bound_id
|
|
: inst_id;
|
|
return ValidateArrayType(context, SemIR::LocId(error_loc), inst)
|
|
? ConstantEvalResult::NewSamePhase(inst)
|
|
: ConstantEvalResult::Error;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::AsCompatible inst)
|
|
-> ConstantEvalResult {
|
|
// AsCompatible changes the type of the source instruction; its constant
|
|
// value, if there is one, needs to be modified to be of the same type.
|
|
auto value_id = context.constant_values().Get(inst.source_id);
|
|
CARBON_CHECK(value_id.is_constant());
|
|
|
|
auto value_inst =
|
|
context.insts().Get(context.constant_values().GetInstId(value_id));
|
|
value_inst.SetType(inst.type_id);
|
|
return ConstantEvalResult::NewAnyPhase(value_inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::AliasBinding inst)
|
|
-> ConstantEvalResult {
|
|
// An alias evaluates to the value it's bound to.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::WrapperBinding inst)
|
|
-> ConstantEvalResult {
|
|
// A reference binding evaluates to the value it's bound to.
|
|
if (inst.value_id.has_value() && SemIR::IsRefCategory(SemIR::GetExprCategory(
|
|
context.sem_ir(), inst.value_id))) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::AcquireValue inst) -> ConstantEvalResult {
|
|
SemIR::ConstantId const_id = SemIR::ConstantId::NotConstant;
|
|
if (const auto* var_decl = GetAsClangVarDecl(context, inst.value_id)) {
|
|
const_id =
|
|
EvalCppVarDecl(context, SemIR::LocId(inst_id), var_decl, inst.type_id);
|
|
} else if (auto temporary =
|
|
context.insts().TryGetAs<SemIR::Temporary>(inst.value_id)) {
|
|
const_id = context.constant_values().Get(temporary->init_id);
|
|
}
|
|
|
|
if (const_id.has_value() && const_id.is_constant()) {
|
|
return ConstantEvalResult::Existing(const_id);
|
|
}
|
|
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ClassElementAccess inst)
|
|
-> ConstantEvalResult {
|
|
return PerformAggregateAccess(context, inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ClassDecl inst)
|
|
-> ConstantEvalResult {
|
|
const auto& class_info = context.classes().Get(inst.class_id);
|
|
|
|
// If the class has generic parameters, we don't produce a class type, but a
|
|
// callable whose return value is a class type.
|
|
if (class_info.has_parameters()) {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
|
|
}
|
|
|
|
// A non-generic class declaration evaluates to the class type.
|
|
return ConstantEvalResult::NewAnyPhase(SemIR::ClassType{
|
|
.type_id = SemIR::TypeType::TypeId,
|
|
.class_id = inst.class_id,
|
|
.specific_id =
|
|
context.generics().GetSelfSpecific(class_info.generic_id)});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::ClassInit inst)
|
|
-> ConstantEvalResult {
|
|
// TODO: Add a `ClassValue` to represent a constant class object
|
|
// representation instead of using a `StructValue`.
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ConstType inst)
|
|
-> ConstantEvalResult {
|
|
// `const (const T)` evaluates to `const T`.
|
|
if (context.insts().Is<SemIR::ConstType>(inst.inner_id)) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.inner_id));
|
|
}
|
|
// Otherwise, `const T` evaluates to itself.
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::PartialType inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::Converted inst)
|
|
-> ConstantEvalResult {
|
|
// A conversion evaluates to the result of the conversion.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.result_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::Deref /*inst*/)
|
|
-> ConstantEvalResult {
|
|
// TODO: Handle this.
|
|
return ConstantEvalResult::TODO;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ExportDecl inst)
|
|
-> ConstantEvalResult {
|
|
// An export instruction evaluates to the exported declaration.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::FacetAccessType inst)
|
|
-> ConstantEvalResult {
|
|
auto facet = context.insts().Get(inst.facet_value_inst_id);
|
|
CARBON_CHECK(context.types().Is<SemIR::FacetType>(facet.type_id()));
|
|
|
|
// If the facet is a `type`, we can evaluate to the facet.
|
|
if (facet.type_id() == SemIR::TypeType::TypeId) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.facet_value_inst_id));
|
|
}
|
|
|
|
// If the facet is a FacetValue, it wraps a `type`, and we can evaluate to
|
|
// that `type`.
|
|
if (auto facet_value = facet.TryAs<SemIR::FacetValue>()) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(facet_value->type_inst_id));
|
|
}
|
|
|
|
// Other instructions (e.g. ImplWitnessAccess) of type `FacetType` can appear
|
|
// here, in which case the constant inst is a `FacetAccessType` until those
|
|
// instructions resolve to one of the above.
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::FacetValue inst)
|
|
-> ConstantEvalResult {
|
|
// If the FacetValue is of type `type`, then it evaluates to the type inside
|
|
// it.
|
|
if (inst.type_id == SemIR::TypeType::TypeId) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.type_inst_id));
|
|
}
|
|
|
|
// A FacetValue that just wraps a facet without adding/removing any witnesses
|
|
// (which means they have the same type) is evaluated to the facet itself.
|
|
if (auto access =
|
|
context.insts().TryGetAs<SemIR::FacetAccessType>(inst.type_inst_id)) {
|
|
if (context.insts().Get(access->facet_value_inst_id).type_id() ==
|
|
inst.type_id) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(access->facet_value_inst_id));
|
|
}
|
|
}
|
|
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::FloatType inst) -> ConstantEvalResult {
|
|
return ValidateFloatTypeAndSetKind(context, SemIR::LocId(inst_id), inst)
|
|
? ConstantEvalResult::NewSamePhase(inst)
|
|
: ConstantEvalResult::Error;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::FunctionDecl inst)
|
|
-> ConstantEvalResult {
|
|
// A function declaration evaluates to a function object, which is an empty
|
|
// object of function type.
|
|
// TODO: Eventually we may need to handle captures here.
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ImplSelfWitness inst)
|
|
-> ConstantEvalResult {
|
|
// Canonicalize the self in the same way as LookupImplWitness.
|
|
inst.period_self = context.constant_values().GetInstId(
|
|
GetCanonicalQuerySelfForLookupImplWitness(
|
|
context, context.constant_values().Get(inst.period_self), nullptr));
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::LookupImplWitness inst) -> ConstantEvalResult {
|
|
// Canonicalize the query self to reduce the number of unique witness
|
|
// instructions and enable constant value comparisons.
|
|
auto self_facet_value_inst_id = SemIR::InstId::None;
|
|
inst.query_self_inst_id = context.constant_values().GetInstId(
|
|
GetCanonicalQuerySelfForLookupImplWitness(
|
|
context, context.constant_values().Get(inst.query_self_inst_id),
|
|
&self_facet_value_inst_id));
|
|
|
|
auto witness_id = EvalLookupSingleFinalWitness(context, SemIR::LocId(inst_id),
|
|
inst, self_facet_value_inst_id,
|
|
EvalImplLookupMode::Normal);
|
|
if (witness_id == SemIR::ErrorInst::ConstantId) {
|
|
return ConstantEvalResult::Error;
|
|
}
|
|
if (witness_id.has_value()) {
|
|
return ConstantEvalResult::Existing(witness_id);
|
|
}
|
|
|
|
// Try again when the query is modified by a specific.
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
// Given a SpecificInterface and an index of an associated constant in that
|
|
// interface, try find a value for that constant in the rewrite constraints of
|
|
// the type of `search_facet`.
|
|
static auto TryFindValueInRewriteConstraints(
|
|
Context& context, SemIR::LocId loc_id,
|
|
llvm::ArrayRef<SemIR::ImplWitnessAccess> access_qualifiers,
|
|
SemIR::SpecificInterfaceId specific_interface_id,
|
|
SemIR::ElementIndex interface_index, SemIR::InstId search_facet)
|
|
-> SemIR::ConstantId {
|
|
auto access_self_type_id = context.insts().Get(search_facet).type_id();
|
|
if (access_self_type_id == SemIR::TypeType::TypeId) {
|
|
// A self facet of type `type` has no rewrite constraints to look in.
|
|
return SemIR::ConstantId::None;
|
|
}
|
|
|
|
auto access_identified_facet_type_id = TryToIdentifyFacetType(
|
|
context, loc_id, context.constant_values().Get(search_facet),
|
|
access_self_type_id, true);
|
|
if (!access_identified_facet_type_id.has_value()) {
|
|
return SemIR::ConstantId::None;
|
|
}
|
|
const auto& access_identified =
|
|
context.identified_facet_types().Get(access_identified_facet_type_id);
|
|
|
|
// TODO: We could consider something better than linear search here, such as a
|
|
// map. However that would probably require heap allocations which may be
|
|
// worse overall since the number of rewrite constraints is generally low. If
|
|
// the `rewrite_constraints` were sorted so that associated constants are
|
|
// grouped together, as in ResolveFacetTypeRewriteConstraints(), and limited
|
|
// to just the `ImplWitnessAccess` entries, then a binary search may work
|
|
// here.
|
|
//
|
|
// Look at each rewrite constraint available in the identified facet type of
|
|
// the `search_facet`. If we find a rewrite into `specific_interface_id`, we
|
|
// can evaluate to the RHS of that rewrite.
|
|
for (auto rewrite : access_identified.rewrites()) {
|
|
auto rewrite_lhs_access =
|
|
context.constant_values().TryGetInstAs<SemIR::ImplWitnessAccess>(
|
|
rewrite.lhs.RawValue());
|
|
if (!rewrite_lhs_access) {
|
|
continue;
|
|
}
|
|
if (rewrite_lhs_access->index != interface_index) {
|
|
continue;
|
|
}
|
|
|
|
auto rewrite_lhs_witness = context.insts().GetAs<SemIR::IdentifiedWitness>(
|
|
rewrite_lhs_access->witness_id);
|
|
|
|
// The LHS of a rewrite can be an arbitrary type. For example:
|
|
// ```
|
|
// T: Z where C impls (Y(.Self) where .Y1 = {})
|
|
// ```
|
|
// The rewrite in the facet type will be `(C as Y(T)).Y1 = {}`.
|
|
//
|
|
// It can also be another ImplWitnessAccess. For example:
|
|
// ```
|
|
// T: Z where .Z1 impls (Y where .Y1 = {})
|
|
// ```
|
|
// The rewrite in the facet type will be `((T as Z).Z1 as Y).Y1 = {}`.
|
|
//
|
|
// If the original access was `T.Z1.Y1`, by the time `search_facet` is `T`
|
|
// where we find the rewrite, we will have already tried and failed to find
|
|
// a witness in `T.Z1`. So now a rewrite must be `.Z1.Y1` not just `.Y1`.
|
|
// This is represented in the `access_qualifiers`, and we check that the LHS
|
|
// of the rewrite contains those qualifiers in its access self facets,
|
|
// requiring that they are accessing the same interface and element.
|
|
//
|
|
// The inner-most self facet in the rewrite must be `.Self` in order to be
|
|
// providing a value for `search_facet`.
|
|
//
|
|
// TODO: Requiring the rewrite to be against `.Self` is actually
|
|
// insufficient. For a facet like
|
|
// ```
|
|
// T: type where C impls (Z(.Self) where .Z1 = ())
|
|
// ```
|
|
// and an access like `C.(Z(T).Z1)`, the root of the access is `C`. If we
|
|
// search `T` for a witness, we'd need the inner-most self facet to be `C`
|
|
// in the rewrite. This isn't an issue in the code right now, because we
|
|
// only ever search the facet type for the self facets in the
|
|
// ImplWitnessAccess. But we should also be searching facets in the specific
|
|
// interfaces that would be part of the type structure. That is, we should
|
|
// match the search in `CollectFacetWitnessSources()`.
|
|
auto rewrite_lhs_self = rewrite_lhs_witness.query_self_inst_id;
|
|
bool mismatching_qualifier = false;
|
|
for (auto qualifier : access_qualifiers) {
|
|
auto rewrite_lhs_access =
|
|
context.insts().TryGetAs<SemIR::ImplWitnessAccess>(rewrite_lhs_self);
|
|
if (!rewrite_lhs_access) {
|
|
mismatching_qualifier = true;
|
|
break;
|
|
}
|
|
if (rewrite_lhs_access->index != qualifier.index) {
|
|
mismatching_qualifier = true;
|
|
break;
|
|
}
|
|
auto rewrite_lhs_witness =
|
|
context.insts().GetAs<SemIR::IdentifiedWitness>(
|
|
rewrite_lhs_access->witness_id);
|
|
auto qualifier_witness =
|
|
context.insts().GetAs<SemIR::IdentifiedWitness>(qualifier.witness_id);
|
|
|
|
auto rewrite_lhs_interface = EvaluateIdentifiedWitnesses(
|
|
context, loc_id,
|
|
context.specific_interfaces().Get(
|
|
rewrite_lhs_witness.query_specific_interface_id));
|
|
auto qualifier_interface = EvaluateIdentifiedWitnesses(
|
|
context, loc_id,
|
|
context.specific_interfaces().Get(
|
|
qualifier_witness.query_specific_interface_id));
|
|
|
|
if (rewrite_lhs_interface != qualifier_interface) {
|
|
mismatching_qualifier = true;
|
|
break;
|
|
}
|
|
rewrite_lhs_self = rewrite_lhs_witness.query_self_inst_id;
|
|
}
|
|
if (mismatching_qualifier) {
|
|
continue;
|
|
}
|
|
|
|
auto rewrite_lhs_interface = EvaluateIdentifiedWitnesses(
|
|
context, loc_id,
|
|
context.specific_interfaces().Get(
|
|
rewrite_lhs_witness.query_specific_interface_id));
|
|
|
|
if (rewrite_lhs_interface !=
|
|
context.specific_interfaces().Get(specific_interface_id)) {
|
|
// This rewrite is into a different interface than the access query.
|
|
continue;
|
|
}
|
|
|
|
return rewrite.rhs.EvaluateIdentifiedWitnesses(context, loc_id);
|
|
}
|
|
|
|
return SemIR::ConstantId::None;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::ImplWitnessAccess inst) -> ConstantEvalResult {
|
|
CARBON_DIAGNOSTIC(ImplAccessMemberBeforeSet, Error,
|
|
"accessing member from impl before it has a defined value");
|
|
CARBON_KIND_SWITCH(context.insts().Get(inst.witness_id)) {
|
|
case CARBON_KIND(SemIR::ImplWitness witness): {
|
|
// This is PerformAggregateAccess followed by GetConstantValueInSpecific.
|
|
auto witness_table = context.insts().GetAs<SemIR::ImplWitnessTable>(
|
|
witness.witness_table_id);
|
|
auto elements = context.inst_blocks().Get(witness_table.elements_id);
|
|
// `elements` can be empty if there is only a forward declaration of the
|
|
// impl.
|
|
if (!elements.empty()) {
|
|
auto index = static_cast<size_t>(inst.index.index);
|
|
CARBON_CHECK(index < elements.size(), "Access out of bounds.");
|
|
auto element = elements[index];
|
|
if (element.has_value() &&
|
|
element != SemIR::InstId::ImplWitnessTablePlaceholder) {
|
|
LoadImportRef(context, element);
|
|
// The constant values in the witness table may depend on the ImplDecl
|
|
// specific definition, which we must ensure is resolved before
|
|
// accessing them.
|
|
if (witness.specific_id.has_value()) {
|
|
ResolveSpecificDefinition(context, SemIR::LocId(inst_id),
|
|
witness.specific_id);
|
|
}
|
|
return ConstantEvalResult::Existing(GetConstantValueInSpecific(
|
|
context.sem_ir(), witness.specific_id, element));
|
|
}
|
|
}
|
|
// If we get here, this impl witness table entry has not been populated
|
|
// yet, because the impl was referenced within its own definition.
|
|
// TODO: Add note pointing to the impl declaration.
|
|
context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet);
|
|
return ConstantEvalResult::Error;
|
|
}
|
|
case CARBON_KIND(SemIR::CustomWitness custom_witness): {
|
|
auto elements = context.inst_blocks().Get(custom_witness.elements_id);
|
|
auto index = static_cast<size_t>(inst.index.index);
|
|
// `elements` can be shorter than the number of associated entities while
|
|
// we're building the synthetic witness.
|
|
if (index < elements.size()) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(elements[index]));
|
|
}
|
|
// If we get here, this synthesized witness table entry has not been
|
|
// populated yet.
|
|
// TODO: Is this reachable? We have no test coverage for this diagnostic.
|
|
context.emitter().Emit(inst_id, ImplAccessMemberBeforeSet);
|
|
return ConstantEvalResult::Error;
|
|
}
|
|
case CARBON_KIND(SemIR::LookupImplWitness witness): {
|
|
// If the witness is symbolic but has a self type that is a FacetType, it
|
|
// can pull rewrite values from the self's facet type. If the access is
|
|
// for one of those rewrites, evaluate to the RHS of the rewrite.
|
|
//
|
|
// If we have a nested `.X1.Y1.Z1` we start with the facet type of .Y1 to
|
|
// look for a rewrite constraint that provides the value for .Z1. But if
|
|
// we don't find it, we try .X1 and .Self.
|
|
llvm::SmallVector<SemIR::ImplWitnessAccess> access_qualifiers;
|
|
auto search_facet = witness.query_self_inst_id;
|
|
while (true) {
|
|
auto const_id = TryFindValueInRewriteConstraints(
|
|
context, SemIR::LocId(inst_id), access_qualifiers,
|
|
witness.query_specific_interface_id, inst.index, search_facet);
|
|
if (const_id.has_value()) {
|
|
return ConstantEvalResult::Existing(const_id);
|
|
}
|
|
if (auto access = context.insts().TryGetAs<SemIR::ImplWitnessAccess>(
|
|
search_facet)) {
|
|
auto witness = context.insts().GetAs<SemIR::LookupImplWitness>(
|
|
access->witness_id);
|
|
access_qualifiers.push_back(*access);
|
|
search_facet = witness.query_self_inst_id;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context,
|
|
SemIR::ImplWitnessAccessSubstituted inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context,
|
|
SemIR::ImplWitnessAssociatedConstant inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.inst_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::ImportRefUnloaded inst)
|
|
-> ConstantEvalResult {
|
|
CARBON_FATAL("ImportRefUnloaded should be loaded before TryEvalInst: {0}",
|
|
inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InPlaceInit inst)
|
|
-> ConstantEvalResult {
|
|
// Initialization is not performed in-place during constant evaluation, so
|
|
// just return the value of the initializer.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.src_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::IntType inst) -> ConstantEvalResult {
|
|
return ValidateIntType(context, SemIR::LocId(inst_id), inst)
|
|
? ConstantEvalResult::NewSamePhase(inst)
|
|
: ConstantEvalResult::Error;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InterfaceDecl inst)
|
|
-> ConstantEvalResult {
|
|
const auto& interface_info = context.interfaces().Get(inst.interface_id);
|
|
|
|
// If the interface has generic parameters, we don't produce an interface
|
|
// type, but a callable whose return value is an interface type.
|
|
if (interface_info.has_parameters()) {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
|
|
}
|
|
|
|
// A non-parameterized interface declaration evaluates to a declared facet
|
|
// type containing just the interface.
|
|
return ConstantEvalResult::NewAnyPhase(FacetTypeFromInterface(
|
|
context, inst.interface_id,
|
|
context.generics().GetSelfSpecific(interface_info.generic_id)));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::MarkInPlaceInit inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.src_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::NamedConstraintDecl inst)
|
|
-> ConstantEvalResult {
|
|
const auto& named_constraint_info =
|
|
context.named_constraints().Get(inst.named_constraint_id);
|
|
|
|
// If the named constraint has generic parameters, we don't produce a named
|
|
// constraint type, but a callable whose return value is a named constraint
|
|
// type.
|
|
if (named_constraint_info.has_parameters()) {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
|
|
}
|
|
|
|
// A non-parameterized named constraint declaration evaluates to a declared
|
|
// facet type containing just the named constraint.
|
|
return ConstantEvalResult::NewAnyPhase(FacetTypeFromNamedConstraint(
|
|
context, inst.named_constraint_id,
|
|
context.generics().GetSelfSpecific(named_constraint_info.generic_id)));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::NameRef inst)
|
|
-> ConstantEvalResult {
|
|
// A name reference evaluates to the value the name resolves to.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::RequireCompleteType inst) -> ConstantEvalResult {
|
|
auto witness_type_id =
|
|
GetSingletonType(context, SemIR::WitnessType::TypeInstId);
|
|
|
|
// If the type is a concrete constant, require it to be complete now.
|
|
auto complete_type_id =
|
|
context.types().GetTypeIdForTypeInstId(inst.complete_type_inst_id);
|
|
if (complete_type_id.is_concrete()) {
|
|
Diagnostics::ContextScope diagnostic_context(
|
|
&context.emitter(), [&](auto& builder) {
|
|
CARBON_DIAGNOSTIC_CONTEXT(IncompleteTypeInMonomorphization,
|
|
"{0} evaluates to incomplete type {1}",
|
|
InstIdAsType, InstIdAsType);
|
|
builder.Attach(inst_id, IncompleteTypeInMonomorphization,
|
|
context.insts()
|
|
.GetAs<SemIR::RequireCompleteType>(inst_id)
|
|
.complete_type_inst_id,
|
|
inst.complete_type_inst_id);
|
|
});
|
|
// We use TryToCompleteType() instead of RequireCompleteType() because we
|
|
// are currently evaluating a RequireCompleteType instruction, and calling
|
|
// RequireCompleteType() would insert another copy of the same instruction.
|
|
if (!TryToCompleteType(context, complete_type_id, SemIR::LocId(inst_id),
|
|
true)) {
|
|
return ConstantEvalResult::Error;
|
|
}
|
|
return ConstantEvalResult::NewAnyPhase(SemIR::CompleteTypeWitness{
|
|
.type_id = witness_type_id,
|
|
.object_repr_type_inst_id = context.types().GetTypeInstId(
|
|
context.types().GetObjectRepr(complete_type_id))});
|
|
}
|
|
|
|
// If it's not a concrete constant, require it to be complete once it
|
|
// becomes one.
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::SpecificConstant inst) -> ConstantEvalResult {
|
|
// The SpecificConstant can refer to a constant in the definition region of
|
|
// the generic. This can happen during substitution. If it does, resolve the
|
|
// definition region now.
|
|
//
|
|
// TODO: This is somewhat unprincipled; it's not clear this is the right way
|
|
// to address this problem. We mostly don't need this because eval blocks for
|
|
// generics contain instructions to cause specifics to be resolved as needed,
|
|
// but substitution doesn't run those. Other options to consider:
|
|
//
|
|
// * Detect this case in Subst and resolve the specific there instead. This
|
|
// could be limited to the case where we are substituting into a
|
|
// non-canonical instruction.
|
|
// * Move away from using Subst in general, and rely on eval blocks
|
|
// containing instructions to resolve specifics as needed. This would
|
|
// require us to build more generics, for contexts where we currently use
|
|
// Subst but could form a specific instead.
|
|
auto const_id = context.constant_values().GetAttached(inst.inst_id);
|
|
if (const_id.has_value() && const_id.is_symbolic()) {
|
|
const auto& symbolic_const =
|
|
context.constant_values().GetSymbolicConstant(const_id);
|
|
if (symbolic_const.index.has_value() &&
|
|
symbolic_const.index.region() == SemIR::GenericInstIndex::Definition) {
|
|
ResolveSpecificDefinition(context, SemIR::LocId(inst_id),
|
|
inst.specific_id);
|
|
}
|
|
}
|
|
|
|
// Pull the constant value out of the specific.
|
|
return ConstantEvalResult::Existing(SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), inst.specific_id, inst.inst_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::SpecificImplFunction inst) -> ConstantEvalResult {
|
|
auto callee_inst = context.insts().Get(inst.callee_id);
|
|
// If the callee is not a function value, we're not ready to evaluate this
|
|
// yet. Build a symbolic `SpecificImplFunction` constant.
|
|
if (!callee_inst.Is<SemIR::StructValue>()) {
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
auto callee_type_id = callee_inst.type_id();
|
|
auto callee_fn_type =
|
|
context.types().TryGetAs<SemIR::FunctionType>(callee_type_id);
|
|
if (!callee_fn_type) {
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
// If the callee function found in the impl witness is not generic, the result
|
|
// is simply that function.
|
|
// TODO: We could do this even before the callee is concrete.
|
|
auto generic_id =
|
|
context.functions().Get(callee_fn_type->function_id).generic_id;
|
|
if (!generic_id.has_value()) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.callee_id));
|
|
}
|
|
|
|
// Find the arguments to use.
|
|
auto enclosing_specific_id = callee_fn_type->specific_id;
|
|
auto enclosing_args = context.inst_blocks().Get(
|
|
context.specifics().GetArgsOrEmpty(enclosing_specific_id));
|
|
auto interface_fn_args = context.inst_blocks().Get(
|
|
context.specifics().GetArgsOrEmpty(inst.specific_id));
|
|
|
|
// Form new specific for the generic callee function. The arguments for this
|
|
// specific are the enclosing arguments of the callee followed by the
|
|
// remaining arguments from the interface function. Impl checking has ensured
|
|
// that these arguments can also be used for the function in the impl witness.
|
|
auto num_params = context.inst_blocks()
|
|
.Get(context.generics().Get(generic_id).bindings_id)
|
|
.size();
|
|
llvm::SmallVector<SemIR::InstId> args;
|
|
args.reserve(num_params);
|
|
args.append(enclosing_args.begin(), enclosing_args.end());
|
|
int remaining_params = num_params - args.size();
|
|
CARBON_CHECK(static_cast<int>(interface_fn_args.size()) >= remaining_params);
|
|
args.append(interface_fn_args.end() - remaining_params,
|
|
interface_fn_args.end());
|
|
auto specific_id =
|
|
MakeSpecific(context, SemIR::LocId(inst_id), generic_id, args);
|
|
context.definitions_required_by_use().push_back(
|
|
{SemIR::LocId(inst_id), specific_id});
|
|
|
|
return ConstantEvalResult::NewSamePhase(
|
|
SemIR::SpecificFunction{.type_id = inst.type_id,
|
|
.callee_id = inst.callee_id,
|
|
.specific_id = specific_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::SpecificInst inst)
|
|
-> ConstantEvalResult {
|
|
// Pull the constant value out of the specific.
|
|
return ConstantEvalResult::Existing(SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), inst.specific_id, inst.inst_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::SpecificFunction inst) -> ConstantEvalResult {
|
|
auto callee_function =
|
|
SemIR::GetCalleeAsFunction(context.sem_ir(), inst.callee_id);
|
|
const auto& fn = context.functions().Get(callee_function.function_id);
|
|
if (!callee_function.self_type_id.has_value() &&
|
|
fn.GetBuiltinFunctionKind(context.sem_ir()) !=
|
|
SemIR::BuiltinFunctionKind::NoOp &&
|
|
fn.virtual_modifier != SemIR::Function::VirtualModifier::Abstract) {
|
|
// This is not an associated function. Those will be required to be defined
|
|
// as part of checking that the impl is complete.
|
|
context.definitions_required_by_use().push_back(
|
|
{SemIR::LocId(inst_id), inst.specific_id});
|
|
}
|
|
// Create new constant for a specific function.
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::SpliceBlock inst)
|
|
-> ConstantEvalResult {
|
|
// SpliceBlock evaluates to the result value that is (typically) within the
|
|
// block. This can be constant even if the block contains other non-constant
|
|
// instructions.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.result_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::SpliceInst inst)
|
|
-> ConstantEvalResult {
|
|
// The constant value of a SpliceInst is the constant value of the instruction
|
|
// being spliced. Note that `inst.inst_id` is the instruction being spliced,
|
|
// so we need to go through another round of obtaining the constant value in
|
|
// addition to the one performed by the eval infrastructure.
|
|
auto nested_inst = context.insts().Get(inst.inst_id);
|
|
if (auto inst_value = nested_inst.TryAs<SemIR::InstValue>()) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst_value->inst_id));
|
|
}
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::StructAccess inst)
|
|
-> ConstantEvalResult {
|
|
return PerformAggregateAccess(context, inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::StructInit inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::StructLiteral inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
|
|
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::TemplateInst inst)
|
|
-> ConstantEvalResult {
|
|
auto const_id = context.constant_values().Get(inst.inst_id);
|
|
if (const_id.is_concrete()) {
|
|
return ConstantEvalResult::Existing(const_id);
|
|
}
|
|
|
|
return ConstantEvalResult::NewAnyPhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::TupleAccess inst)
|
|
-> ConstantEvalResult {
|
|
return PerformAggregateAccess(context, inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::TupleInit inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::TupleValue{
|
|
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::TupleLiteral inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::NewSamePhase(SemIR::TupleValue{
|
|
.type_id = inst.type_id, .elements_id = inst.elements_id});
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::TypeComponentOf inst)
|
|
-> ConstantEvalResult {
|
|
auto form_constant_inst_id =
|
|
context.constant_values().GetConstantInstId(inst.form_inst_id);
|
|
if (auto primitive_form = context.insts().TryGetAs<SemIR::AnyPrimitiveForm>(
|
|
form_constant_inst_id)) {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(primitive_form->type_component_id));
|
|
}
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::TypeLiteral inst)
|
|
-> ConstantEvalResult {
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.value_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::TypeOfInst inst)
|
|
-> ConstantEvalResult {
|
|
// Grab the type from the instruction produced as our operand.
|
|
if (auto inst_value =
|
|
context.insts().TryGetAs<SemIR::InstValue>(inst.inst_id)) {
|
|
return ConstantEvalResult::Existing(context.types().GetConstantId(
|
|
context.insts().Get(inst_value->inst_id).type_id()));
|
|
}
|
|
return ConstantEvalResult::NewSamePhase(inst);
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::UnaryOperatorNot inst)
|
|
-> ConstantEvalResult {
|
|
// `not true` -> `false`, `not false` -> `true`.
|
|
// All other uses of unary `not` are non-constant.
|
|
auto const_id = context.constant_values().Get(inst.operand_id);
|
|
if (const_id.is_concrete()) {
|
|
auto value =
|
|
context.constant_values().GetInstAs<SemIR::BoolLiteral>(const_id);
|
|
value.value = SemIR::BoolValue::From(!value.value.ToBool());
|
|
return ConstantEvalResult::NewSamePhase(value);
|
|
}
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& /*context*/, SemIR::UpdateInit /*inst*/)
|
|
-> ConstantEvalResult {
|
|
// TODO: Support folding together a ClassInit with an update that sets the
|
|
// vptr.
|
|
return ConstantEvalResult::TODO;
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::ValueOfInitializer inst)
|
|
-> ConstantEvalResult {
|
|
// Values of value expressions and initializing expressions are represented in
|
|
// the same way during constant evaluation, so just return the value of the
|
|
// operand.
|
|
return ConstantEvalResult::Existing(
|
|
context.constant_values().Get(inst.init_id));
|
|
}
|
|
|
|
auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
|
|
SemIR::VarStorage inst) -> ConstantEvalResult {
|
|
if (!inst.pattern_id.has_value()) {
|
|
// This variable was not created from a `var` pattern, so isn't a global
|
|
// variable.
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
// A variable is constant if it's global.
|
|
auto entity_name_id = SemIR::GetFirstBindingNameFromPatternId(
|
|
context.sem_ir(), inst.pattern_id);
|
|
if (!entity_name_id.has_value()) {
|
|
// Variable doesn't introduce any bindings, so can only be referenced by its
|
|
// own initializer. We treat such a reference as not being constant.
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
auto scope_id = context.entity_names().Get(entity_name_id).parent_scope_id;
|
|
if (!scope_id.has_value()) {
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
auto scope_inst =
|
|
context.insts().Get(context.name_scopes().Get(scope_id).inst_id());
|
|
if (!scope_inst.Is<SemIR::Namespace>() &&
|
|
!scope_inst.Is<SemIR::ClassDecl>()) {
|
|
// Only namespace-scope and class-scope variables are reference constants.
|
|
// Class-scope variables cannot currently be declared directly, but can
|
|
// occur when static data members are imported from C++.
|
|
return ConstantEvalResult::NotConstant;
|
|
}
|
|
|
|
// This is a constant reference expression denoting this global variable.
|
|
return ConstantEvalResult::Existing(
|
|
SemIR::ConstantId::ForConcreteConstant(inst_id));
|
|
}
|
|
|
|
} // namespace Carbon::Check
|