Files
carbon-lang/toolchain/check/handle_alias.cpp
T
Richard Smith d50576ca16 Handle references to global lets and enclosing locals. (#8061)
When building an expression referencing the result of name lookup, if
the lookup result is non-constant then check we can access it from the
current scope.

* If it is a global, then use a new `GlobalRef` instruction to get the
value of that global. In lowering, create global variables to hold the
values of global `let` bindings so that `GlobalRef` can access those
values.
* If it is a local, walk the lexical scopes between it and the point of
use and ensure it is usable through from all of them. Error if there's
an intervening declarative scope such as a class, add a TODO to handle
captures for intervening functions, and allow `typeof(...)` to name
things it can't read the value of since it's unevaluated.

Global `let`s (which includes `let`s at class scope) now also have their
initializers collected into the global inits block. If the initializer
is non-constant, the wrapper binding also lives in the global inits
block; otherwise it continues to live in the file block and the variable
emitted for it is a global constant.

Assisted-by: Gemini and Claude via Antigravity
2026-11-06 02:53:03 +00:00

103 lines
4.0 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/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/modifiers.h"
#include "toolchain/check/name_component.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
auto HandleParseNode(Context& context, Parse::AliasIntroducerId /*node_id*/)
-> bool {
// Aliases can't be generic, but we might have parsed a generic parameter in
// their name, so enter a generic scope just in case.
StartGenericDecl(context);
// Optional modifiers and the name follow.
context.decl_introducer_state_stack().Push<Lex::TokenKind::Alias>();
// Aliases can't have non-constant targets, but we diagnose that later. Don't
// diagnose references to enclosing non-constant state in name lookup since
// that would produce a less useful error message.
context.decl_name_stack().PushScopeAndStartName(
ScopeStack::NonConstantAccess::Permitted);
return true;
}
auto HandleParseNode(Context& /*context*/,
Parse::AliasInitializerId /*node_id*/) -> bool {
return true;
}
auto HandleParseNode(Context& context, Parse::AliasId /*node_id*/) -> bool {
auto [expr_node, expr_id] = context.node_stack().PopExprWithNodeId();
bool diagnosed_params = false;
auto name_context =
context.decl_name_stack().FinishName(PopNameComponentWithoutParams(
context, Lex::TokenKind::Alias, &diagnosed_params));
if (diagnosed_params) {
// If the alias had generic parameters, they could be SymbolicBindings which
// then appear in the `expr_id`. Using those elsewhere is then invalid and
// creates invalid states in the toolchain.
expr_id = SemIR::ErrorInst::InstId;
}
DiscardGenericDecl(context);
auto introducer =
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Alias>();
LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access);
auto entity_name_id = context.entity_names().Add(
{.name_id = name_context.name_id_for_new_inst(),
.parent_scope_id = name_context.parent_scope_id});
switch (SemIR::GetExprCategory(context.sem_ir(), expr_id)) {
case SemIR::ExprCategory::Value:
case SemIR::ExprCategory::EphemeralRef:
case SemIR::ExprCategory::DurableRef:
case SemIR::ExprCategory::NotExpr:
case SemIR::ExprCategory::Error:
// An alias can refer to a value, a reference, or a non-expression name
// such as a function or namespace.
break;
case SemIR::ExprCategory::ReprInitializing:
case SemIR::ExprCategory::InPlaceInitializing:
case SemIR::ExprCategory::Mixed:
case SemIR::ExprCategory::Dependent:
case SemIR::ExprCategory::RefTagged:
case SemIR::ExprCategory::Pattern:
// For any other kind of expression, convert to a value or reference.
// TODO: Should we allow or require `ref` tagging for reference aliases?
expr_id = ConvertToValueOrRefExpr(context, expr_id);
break;
}
if (!context.constant_values().Get(expr_id).is_constant()) {
CARBON_DIAGNOSTIC(AliasRequiresConstantValue, Error,
"alias refers to a runtime value");
context.emitter().Emit(expr_node, AliasRequiresConstantValue);
}
auto alias_id = AddInst<SemIR::AliasBinding>(
context, name_context.loc_id,
{.type_id = context.insts().Get(expr_id).type_id(),
.entity_name_id = entity_name_id,
.value_id = expr_id});
// Add the name of the binding to the current scope.
context.decl_name_stack().PopScope();
context.decl_name_stack().AddNameOrDiagnose(
name_context, alias_id, introducer.modifier_set.GetAccessKind());
return true;
}
} // namespace Carbon::Check