mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 18:28:11 +00:00
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
103 lines
4.0 KiB
C++
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
|