Files
carbon-lang/toolchain/check/handle_namespace.cpp
T
Jon Ross-Perkins 1f764c8cf1 Implement merging of namespace declarations. (#3647)
Per discussion with zygoloid, namespace declarations will merge both
when repeated in a given file, and across files. This echoes how forward
declarations of other entities are allowed to repeat. When merging with
an imported namespace, fill in the parse node so that future diagnostics
point at the declaration in the same file rather than a declaration in a
different file, just for locality.

Although it might be desirable to issue a diagnostic when a namespace
declaration is repeated within a given file, that's similarly true in
other cases, but may be more desirable as a tidy-style issue rather than
preventing code from compiling. Allowing the repetition also makes the
import versus non-import cases more consistent: the namespace
declarations merge regardless of the source.
2024-01-25 07:29:40 +00:00

58 lines
2.3 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/decl_state.h"
#include "toolchain/check/modifiers.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::Check {
auto HandleNamespaceStart(Context& context,
Parse::NamespaceStartId /*parse_node*/) -> bool {
// Optional modifiers and the name follow.
context.decl_state_stack().Push(DeclState::Namespace);
context.decl_name_stack().PushScopeAndStartName();
return true;
}
auto HandleNamespace(Context& context, Parse::NamespaceId parse_node) -> bool {
auto name_context = context.decl_name_stack().FinishName();
LimitModifiersOnDecl(context, KeywordModifierSet::None,
Lex::TokenKind::Namespace);
auto namespace_inst = SemIR::Namespace{
context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
SemIR::NameScopeId::Invalid, SemIR::InstId::Invalid};
auto namespace_id = context.AddPlaceholderInst({parse_node, namespace_inst});
namespace_inst.name_scope_id = context.name_scopes().Add(
namespace_id, name_context.name_id_for_new_inst(),
name_context.enclosing_scope_id_for_new_inst());
context.ReplaceInstBeforeConstantUse(namespace_id,
{parse_node, namespace_inst});
auto existing_inst_id =
context.decl_name_stack().LookupOrAddName(name_context, namespace_id);
if (existing_inst_id.is_valid()) {
// If there's a name conflict with a namespace, "merge" by using the
// previous declaration. Otherwise, diagnose the issue.
if (auto existing =
context.insts().TryGetAs<SemIR::Namespace>(existing_inst_id)) {
// When the name conflict is an imported namespace, fill the parse node
// so that future diagnostics point at this declaration.
if (existing->import_id.is_valid() &&
!context.insts().GetParseNode(existing_inst_id).is_valid()) {
context.SetNamespaceParseNode(existing_inst_id, parse_node);
}
} else {
context.DiagnoseDuplicateName(namespace_id, existing_inst_id);
}
}
context.decl_name_stack().PopScope();
context.decl_state_stack().Pop(DeclState::Namespace);
return true;
}
} // namespace Carbon::Check