mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
Currently each interface has a `Self` facet internally that becomes a binding to every entity inside the interface: associated constants, functions, and require decls. Each of these has to be independently generic as a result. This makes is challenging in extended name lookup to move into an extended scope of an interface, as we have a specific for the interface, but the names within require a different specific that includes a `Self` facet value. We generalize this relationship by adding a second generic to Interface, called `generic_with_self`. When we want to work with entities inside the interface, we move from the interface-without-specific to the interface-with-self specific by adding a Self to the specific. This is done independently of any particular entity inside the Interface, as those entities are now all members of the interface-with-self generic. Associated constants no longer need a generic of their own, as they do not have separate generic bindings. Functions retain a generic, but if the function has no generic arguments, it will have no bindings of its own now. Require decls retain a generic so that their specific can be instantiated separately from the interface. Requiring the interface to be complete does not require the types in a require decl to be complete unless it is modified by `extend`. So we allow them to be completed later by keeping them in a separate generic. Named constraints look like interfaces and gain the additional inner generic-with-self, with the same relationship to require decls. This removes the need for name lookup to perform Substitution of a Self facet into the extended scope instruction. Instead, the `SpecificConstant` instruction inserted by a `require` decl is part of the interface-with-self generic. When looking through a FacetType for extended scopes, for each interface, we push the scope with the specific for the interface-with-self. Then the constant value of the `SpecificConstant` is correctly modified by the provided self automatically through applying that specific.
131 lines
4.8 KiB
C++
131 lines
4.8 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
|
|
|
|
#ifndef CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_
|
|
|
|
#include "common/array_stack.h"
|
|
#include "common/map.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// A map from an instruction ID representing a canonical symbolic constant to an
|
|
// instruction within an eval block of the generic that computes the specific
|
|
// value for that constant.
|
|
//
|
|
// We arbitrarily use a small size of 256 bytes for the map.
|
|
// TODO: Determine a better number based on measurements.
|
|
using ConstantsInGenericMap = Map<SemIR::InstId, SemIR::InstId, 256>;
|
|
|
|
// A stack of enclosing regions that might be declaring or defining a generic
|
|
// entity. In such a region, we track the generic constructs that are used, such
|
|
// as symbolic constants and types, and instructions that depend on a template
|
|
// parameter.
|
|
//
|
|
// We split a generic into two regions -- declaration and definition -- because
|
|
// these are in general introduced separately, and substituted into separately.
|
|
// For example, for `class C(T:! type, N:! T) { var x: T; }`, a use such as
|
|
// `C(i32, 0)*` substitutes into just the declaration, whereas a use such as
|
|
// `var x: C(i32, 0) = {.x = 0};` also substitutes into the definition.
|
|
class GenericRegionStack {
|
|
public:
|
|
explicit GenericRegionStack(llvm::raw_ostream* vlog_stream)
|
|
: vlog_stream_(vlog_stream) {
|
|
// Reserve a large enough stack that we typically won't need to reallocate.
|
|
constants_in_generic_stack_.reserve(4);
|
|
}
|
|
|
|
struct PendingGeneric {
|
|
// The generic ID. May not have a value if no ID has been assigned yet.
|
|
SemIR::GenericId generic_id;
|
|
// The region of the generic that is being processed.
|
|
SemIR::GenericInstIndex::Region region;
|
|
};
|
|
|
|
// Pushes a region that might be declaring or defining a generic.
|
|
auto Push(PendingGeneric generic) -> void;
|
|
|
|
// Pops a generic region.
|
|
auto Pop() -> void;
|
|
|
|
// Returns whether the stack is empty.
|
|
auto Empty() const -> bool { return pending_generic_ids_.empty(); }
|
|
|
|
// Sets the GenericId for the currently pending generic, once one has been
|
|
// allocated.
|
|
auto SetPendingGenericId(SemIR::GenericId generic_id) -> void {
|
|
CARBON_CHECK(!pending_generic_ids_.back().generic_id.has_value(),
|
|
"Already have a GenericId for the pending generic");
|
|
pending_generic_ids_.back().generic_id = generic_id;
|
|
}
|
|
|
|
// Adds an instruction to the list of instructions whose type or value depends
|
|
// on something in the current pending generic.
|
|
auto AddDependentInst(SemIR::InstId inst_id) -> void {
|
|
CARBON_CHECK(!Empty());
|
|
dependent_inst_stack_.AppendToTop(inst_id);
|
|
}
|
|
|
|
// Adds an instruction to the eval block for the current pending generic.
|
|
auto AddInstToEvalBlock(SemIR::InstId inst_id) -> void {
|
|
CARBON_CHECK(!Empty());
|
|
pending_eval_block_stack_.AppendToTop(inst_id);
|
|
}
|
|
|
|
// Returns the current pending generic.
|
|
auto PeekPendingGeneric() const -> PendingGeneric {
|
|
CARBON_CHECK(!Empty());
|
|
return pending_generic_ids_.back();
|
|
}
|
|
|
|
// Returns the list of dependent instructions in the current generic region.
|
|
auto PeekDependentInsts() -> llvm::ArrayRef<SemIR::InstId> {
|
|
CARBON_CHECK(!Empty());
|
|
return dependent_inst_stack_.PeekArray();
|
|
}
|
|
|
|
// Returns the contents of the eval block for the current generic region.
|
|
auto PeekEvalBlock() -> llvm::ArrayRef<SemIR::InstId> {
|
|
CARBON_CHECK(!Empty());
|
|
return pending_eval_block_stack_.PeekArray();
|
|
}
|
|
|
|
// Returns the mapping from abstract constant instructions to eval block
|
|
// instructions for the current generic.
|
|
auto PeekConstantsInGenericMap() -> ConstantsInGenericMap& {
|
|
CARBON_CHECK(!Empty());
|
|
return constants_in_generic_stack_.back();
|
|
}
|
|
|
|
// Runs verification that the processing cleanly finished.
|
|
auto VerifyOnFinish() const -> void {
|
|
CARBON_CHECK(pending_generic_ids_.empty(),
|
|
"pending_generic_ids_ still has {0} entries",
|
|
pending_generic_ids_.size());
|
|
}
|
|
|
|
private:
|
|
// Whether to print verbose output.
|
|
llvm::raw_ostream* vlog_stream_;
|
|
|
|
// The IDs of pending generics.
|
|
llvm::SmallVector<PendingGeneric> pending_generic_ids_;
|
|
|
|
// Contents of eval blocks for pending generics.
|
|
ArrayStack<SemIR::InstId> pending_eval_block_stack_;
|
|
|
|
// Instructions that depend on the current generic.
|
|
ArrayStack<SemIR::InstId> dependent_inst_stack_;
|
|
|
|
// Mapping from constant InstIds to the corresponding InstIds in the eval
|
|
// blocks for each enclosing generic. We reserve this to a suitable size in
|
|
// the constructor.
|
|
llvm::SmallVector<ConstantsInGenericMap, 0> constants_in_generic_stack_;
|
|
};
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_GENERIC_REGION_STACK_H_
|