From 68e15c37a3fea5f52439a724a06d1ddcf9ffbfc7 Mon Sep 17 00:00:00 2001 From: Manmeet Singh <49202620+Maan2003@users.noreply.github.com> Date: Tue, 18 Apr 2023 04:26:31 +0530 Subject: [PATCH] feat: implement namespace alias (#2720) Fixes https://github.com/carbon-language/carbon-lang/blob/46503c0a9d20d8e38dde1ff4266f4e2b98699669/explorer/interpreter/resolve_names.cpp#L281 This was super fun to implement as first contribution. Learnt a lot about how the interpreter works while digging through the code and trying different ways to fix it. --- explorer/ast/declaration.h | 12 ++++++++ explorer/interpreter/resolve_names.cpp | 27 ++++++++++++++---- explorer/interpreter/type_checker.cpp | 12 +++++--- .../fail_namespace_alias_member.carbon | 2 +- .../fail_wrong_fn_alias_use.carbon} | 13 ++++----- .../alias/fail_wrong_fn_alias_use_expr.carbon | 17 +++++++++++ .../fail_wrong_interface_alias_use.carbon | 17 +++++++++++ .../fail_wrong_namepsace_alias_use.carbon | 17 +++++++++++ .../testdata/alias/namespace_alias.carbon | 28 +++++++++++++++++++ .../alias/namespace_alias_recursive.carbon | 22 +++++++++++++++ 10 files changed, 149 insertions(+), 18 deletions(-) rename explorer/testdata/{namespace => alias}/fail_namespace_alias_member.carbon (75%) rename explorer/testdata/{namespace/fail_namespace_alias.carbon => alias/fail_wrong_fn_alias_use.carbon} (58%) create mode 100644 explorer/testdata/alias/fail_wrong_fn_alias_use_expr.carbon create mode 100644 explorer/testdata/alias/fail_wrong_interface_alias_use.carbon create mode 100644 explorer/testdata/alias/fail_wrong_namepsace_alias_use.carbon create mode 100644 explorer/testdata/alias/namespace_alias.carbon create mode 100644 explorer/testdata/alias/namespace_alias_recursive.carbon diff --git a/explorer/ast/declaration.h b/explorer/ast/declaration.h index 90ac321a1cac..8ed6d9d62e7e 100644 --- a/explorer/ast/declaration.h +++ b/explorer/ast/declaration.h @@ -1016,10 +1016,22 @@ class AliasDeclaration : public Declaration { auto expression_category() const -> ExpressionCategory { return ExpressionCategory::Value; } + // Sets the resolved declaration of alias target. Should only be called once, + // during name resolution. + void set_resolved_declaration(Nonnull decl) { + CARBON_CHECK(!resolved_declaration_.has_value()); + resolved_declaration_ = decl; + } + // Get the resolved declaration of alias target, if any exists. + auto resolved_declaration() const + -> std::optional> { + return resolved_declaration_; + } private: DeclaredName name_; Nonnull target_; + std::optional> resolved_declaration_; }; // Return the unqualified name of a declaration, if it has one. diff --git a/explorer/interpreter/resolve_names.cpp b/explorer/interpreter/resolve_names.cpp index 0e2c2e2ee709..5dddca13fc44 100644 --- a/explorer/interpreter/resolve_names.cpp +++ b/explorer/interpreter/resolve_names.cpp @@ -275,15 +275,26 @@ auto NameResolver::ResolveNames(Expression& expression, break; } case ExpressionKind::SimpleMemberAccessExpression: { + // If the left-hand side of the `.` is a namespace or alias to namespace, + // resolve the name. auto& access = cast(expression); CARBON_ASSIGN_OR_RETURN(std::optional scope, ResolveNames(access.object(), enclosing_scope)); + if (!scope) { + break; + } - // If the left-hand side of the `.` is a namespace, resolve the name. - // TODO: Look through aliases. - if (scope && isa(scope->base())) { - auto ns_it = - namespace_scopes_.find(&cast(scope->base())); + Nonnull base = &scope->base(); + // recursively resolve aliases. + while (const auto* alias = dyn_cast(base)) { + if (auto resolved = alias->resolved_declaration()) { + base = *resolved; + } else { + break; + } + } + if (const auto* namespace_decl = dyn_cast(base)) { + auto ns_it = namespace_scopes_.find(namespace_decl); CARBON_CHECK(ns_it != namespace_scopes_.end()) << "name resolved to undeclared namespace"; CARBON_ASSIGN_OR_RETURN( @@ -859,7 +870,11 @@ auto NameResolver::ResolveNames(Declaration& declaration, CARBON_ASSIGN_OR_RETURN(Nonnull scope, ResolveQualifier(alias.name(), enclosing_scope)); scope->MarkDeclared(alias.name().inner_name()); - CARBON_RETURN_IF_ERROR(ResolveNames(alias.target(), *scope)); + CARBON_ASSIGN_OR_RETURN(auto target, + ResolveNames(alias.target(), *scope)); + if (target && isa(target->base())) { + alias.set_resolved_declaration(&cast(target->base())); + } scope->MarkUsable(alias.name().inner_name()); break; } diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 7cf670b12b9d..076984862392 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -5940,11 +5940,15 @@ auto TypeChecker::DeclareAliasDeclaration(Nonnull alias, << "invalid target for alias declaration"; } - CARBON_ASSIGN_OR_RETURN(Nonnull target, - InterpExp(&alias->target(), arena_, trace_stream_)); - alias->set_static_type(&alias->target().static_type()); - alias->set_constant_value(target); + // constant_value not needed for namespace alias because these are resolved by + // NameResolver + if (alias->target().static_type().kind() != + Value::Kind::TypeOfNamespaceName) { + CARBON_ASSIGN_OR_RETURN(Nonnull target, + InterpExp(&alias->target(), arena_, trace_stream_)); + alias->set_constant_value(target); + } return Success(); } diff --git a/explorer/testdata/namespace/fail_namespace_alias_member.carbon b/explorer/testdata/alias/fail_namespace_alias_member.carbon similarity index 75% rename from explorer/testdata/namespace/fail_namespace_alias_member.carbon rename to explorer/testdata/alias/fail_namespace_alias_member.carbon index 5dfb938eb1f0..eca486e20f45 100644 --- a/explorer/testdata/namespace/fail_namespace_alias_member.carbon +++ b/explorer/testdata/alias/fail_namespace_alias_member.carbon @@ -12,7 +12,7 @@ namespace N; alias M = N; // TODO: It's unclear whether this should be permitted. If not, we should // produce a better diagnostic. -// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/namespace/fail_namespace_alias_member.carbon:[[@LINE+1]]: alias M cannot be used as a name qualifier +// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_namespace_alias_member.carbon:[[@LINE+1]]: alias M cannot be used as a name qualifier fn M.F() {} fn Main() -> i32 { diff --git a/explorer/testdata/namespace/fail_namespace_alias.carbon b/explorer/testdata/alias/fail_wrong_fn_alias_use.carbon similarity index 58% rename from explorer/testdata/namespace/fail_namespace_alias.carbon rename to explorer/testdata/alias/fail_wrong_fn_alias_use.carbon index 25aad855f43b..a10f0cd121ce 100644 --- a/explorer/testdata/namespace/fail_namespace_alias.carbon +++ b/explorer/testdata/alias/fail_wrong_fn_alias_use.carbon @@ -8,13 +8,12 @@ package ExplorerTest api; -namespace N; -fn N.F() {} -// TODO: This should work. -// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/namespace/fail_namespace_alias.carbon:[[@LINE+1]]: could not find `namespace N;` -alias M = N; +fn F() {} +alias A = F; + +// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_wrong_fn_alias_use.carbon:[[@LINE+1]]: alias A cannot be used as a name qualifier +fn A.Foo() {} fn Main() -> i32 { - M.F(); - return 0; + return 0; } diff --git a/explorer/testdata/alias/fail_wrong_fn_alias_use_expr.carbon b/explorer/testdata/alias/fail_wrong_fn_alias_use_expr.carbon new file mode 100644 index 000000000000..3e11eef16f0d --- /dev/null +++ b/explorer/testdata/alias/fail_wrong_fn_alias_use_expr.carbon @@ -0,0 +1,17 @@ +// 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 +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +fn F() {} +alias A = F; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_wrong_fn_alias_use_expr.carbon:[[@LINE+1]]: member access, unexpected fn () -> () in A.x + return A.x; +} diff --git a/explorer/testdata/alias/fail_wrong_interface_alias_use.carbon b/explorer/testdata/alias/fail_wrong_interface_alias_use.carbon new file mode 100644 index 000000000000..ba946b6d19fd --- /dev/null +++ b/explorer/testdata/alias/fail_wrong_interface_alias_use.carbon @@ -0,0 +1,17 @@ +// 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 +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +interface Foo {} +alias A = Foo; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_wrong_interface_alias_use.carbon:[[@LINE+1]]: member access, bar not in interface Foo + return A.bar; +} diff --git a/explorer/testdata/alias/fail_wrong_namepsace_alias_use.carbon b/explorer/testdata/alias/fail_wrong_namepsace_alias_use.carbon new file mode 100644 index 000000000000..77ee8fe745df --- /dev/null +++ b/explorer/testdata/alias/fail_wrong_namepsace_alias_use.carbon @@ -0,0 +1,17 @@ +// 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 +// +// AUTOUPDATE +// RUN: %{not} %{explorer-run} +// RUN: %{not} %{explorer-run-trace} + +package ExplorerTest api; + +namespace Foo; +alias A = Foo; + +fn Main() -> i32 { + // CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_wrong_namepsace_alias_use.carbon:[[@LINE+1]]: expected `.member_name` after name of namespace Foo + return A; +} diff --git a/explorer/testdata/alias/namespace_alias.carbon b/explorer/testdata/alias/namespace_alias.carbon new file mode 100644 index 000000000000..6b6c648157cd --- /dev/null +++ b/explorer/testdata/alias/namespace_alias.carbon @@ -0,0 +1,28 @@ +// 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 +// +// AUTOUPDATE +// RUN: %{explorer-run} +// RUN: %{explorer-run-trace} +// CHECK:STDOUT: result: 0 + +package ExplorerTest api; + +namespace N; +namespace N.M; +fn N.F() {} +fn N.M.FM() {} + +alias A = N; +alias AM = N.M; +alias N.A = N.M; +alias N.A2 = N; + +fn Main() -> i32 { + A.F(); + AM.FM(); + N.A.FM(); + A.A2.A2.A2.A2.A2.A2.A2.F(); + return 0; +} diff --git a/explorer/testdata/alias/namespace_alias_recursive.carbon b/explorer/testdata/alias/namespace_alias_recursive.carbon new file mode 100644 index 000000000000..7171e71a364b --- /dev/null +++ b/explorer/testdata/alias/namespace_alias_recursive.carbon @@ -0,0 +1,22 @@ +// 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 +// +// AUTOUPDATE +// RUN: %{explorer-run} +// RUN: %{explorer-run-trace} +// CHECK:STDOUT: result: 0 + +package ExplorerTest api; + +namespace N; +fn N.F() {} + +alias A = N; +alias B = A; +alias C = B; + +fn Main() -> i32 { + C.F(); + return 0; +}