From 1437b0e26d9e406b88d75e6279eb2f0640c68c80 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 14 Feb 2024 10:33:18 -0800 Subject: [PATCH] Implement 'alias' with transparent semantics. (#3701) Adds `BindAlias` with a hybrid of `BindName` and `NameRef` semantics. I think it's slightly closer to `BindName` because it introduces a name, so I'm going more in that direction. This also matches the need for `bind_name_id` with imports on enclosing scopes. Note, only things that look like a name reference are being allowed on the RHS of `alias`. This includes builtins that look like name references, such as `bool`, but not ones that turn into values underneath, such as `false`. --- toolchain/check/context.cpp | 1 + toolchain/check/decl_state.h | 1 + toolchain/check/eval.cpp | 5 +- toolchain/check/handle_alias.cpp | 75 +++++++++ toolchain/check/import.cpp | 1 + toolchain/check/import_ref.cpp | 22 ++- .../testdata/alias/alias_of_alias.carbon | 29 ++++ .../check/testdata/alias/builtins.carbon | 31 ++++ .../alias/fail_aliased_name_in_diag.carbon | 31 ++++ .../testdata/alias/fail_bool_value.carbon | 26 ++++ .../testdata/alias/fail_control_flow.carbon | 29 ++++ .../alias/fail_local_in_namespace.carbon | 43 ++++++ .../testdata/alias/fail_modifiers.carbon | 42 +++++ .../testdata/alias/fail_name_conflict.carbon | 50 ++++++ .../testdata/alias/fail_not_constant.carbon | 46 ++++++ .../testdata/alias/fail_todo_private.carbon | 18 +++ toolchain/check/testdata/alias/import.carbon | 61 ++++++++ .../check/testdata/alias/import_order.carbon | 88 +++++++++++ .../check/testdata/alias/in_namespace.carbon | 38 +++++ toolchain/check/testdata/alias/local.carbon | 36 +++++ .../check/testdata/class/fail_method.carbon | 53 ++++--- toolchain/check/testdata/class/method.carbon | 146 ++++++++++-------- .../check/testdata/function/call/alias.carbon | 44 ++++++ .../check/testdata/namespace/alias.carbon | 62 ++++++++ .../namespace/fail_decl_in_alias.carbon | 39 +++++ toolchain/diagnostics/diagnostic_kind.def | 6 + toolchain/lower/handle.cpp | 10 ++ toolchain/lower/testdata/alias/local.carbon | 21 +++ toolchain/parse/handle_alias.cpp | 61 ++++++++ toolchain/parse/handle_decl_scope_loop.cpp | 5 + toolchain/parse/handle_statement.cpp | 5 + toolchain/parse/node_kind.def | 11 ++ toolchain/parse/state.def | 34 ++++ toolchain/parse/testdata/alias/basic.carbon | 69 +++++++++ .../parse/testdata/alias/fail_syntax.carbon | 117 ++++++++++++++ toolchain/parse/typed_nodes.h | 19 +++ toolchain/sem_ir/file.cpp | 10 +- toolchain/sem_ir/formatter.cpp | 1 + toolchain/sem_ir/inst_kind.def | 3 +- toolchain/sem_ir/typed_insts.h | 15 +- 40 files changed, 1312 insertions(+), 92 deletions(-) create mode 100644 toolchain/check/handle_alias.cpp create mode 100644 toolchain/check/testdata/alias/alias_of_alias.carbon create mode 100644 toolchain/check/testdata/alias/builtins.carbon create mode 100644 toolchain/check/testdata/alias/fail_aliased_name_in_diag.carbon create mode 100644 toolchain/check/testdata/alias/fail_bool_value.carbon create mode 100644 toolchain/check/testdata/alias/fail_control_flow.carbon create mode 100644 toolchain/check/testdata/alias/fail_local_in_namespace.carbon create mode 100644 toolchain/check/testdata/alias/fail_modifiers.carbon create mode 100644 toolchain/check/testdata/alias/fail_name_conflict.carbon create mode 100644 toolchain/check/testdata/alias/fail_not_constant.carbon create mode 100644 toolchain/check/testdata/alias/fail_todo_private.carbon create mode 100644 toolchain/check/testdata/alias/import.carbon create mode 100644 toolchain/check/testdata/alias/import_order.carbon create mode 100644 toolchain/check/testdata/alias/in_namespace.carbon create mode 100644 toolchain/check/testdata/alias/local.carbon create mode 100644 toolchain/check/testdata/function/call/alias.carbon create mode 100644 toolchain/check/testdata/namespace/alias.carbon create mode 100644 toolchain/check/testdata/namespace/fail_decl_in_alias.carbon create mode 100644 toolchain/lower/testdata/alias/local.carbon create mode 100644 toolchain/parse/handle_alias.cpp create mode 100644 toolchain/parse/testdata/alias/basic.carbon create mode 100644 toolchain/parse/testdata/alias/fail_syntax.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 14014d870b19..3b986b3ff237 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -849,6 +849,7 @@ class TypeCompleter { case SemIR::ArrayInit::Kind: case SemIR::Assign::Kind: case SemIR::BaseDecl::Kind: + case SemIR::BindAlias::Kind: case SemIR::BindName::Kind: case SemIR::BindValue::Kind: case SemIR::BlockArg::Kind: diff --git a/toolchain/check/decl_state.h b/toolchain/check/decl_state.h index dfb741e33a86..b5cf90f64b58 100644 --- a/toolchain/check/decl_state.h +++ b/toolchain/check/decl_state.h @@ -53,6 +53,7 @@ struct DeclState { // The kind of declaration. enum DeclKind : int8_t { FileScope, + Alias, Base, Class, Constraint, diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 58f68a5bd14e..301b28c1d8d5 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -416,7 +416,10 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // are treated as the same value. return SemIR::ConstantId::ForSymbolicConstant(inst_id); - // These semnatic wrappers don't change the constant value. + // These semantic wrappers don't change the constant value. + case SemIR::BindAlias::Kind: + return context.constant_values().Get( + inst.As().value_id); case SemIR::NameRef::Kind: return context.constant_values().Get(inst.As().value_id); case SemIR::Converted::Kind: diff --git a/toolchain/check/handle_alias.cpp b/toolchain/check/handle_alias.cpp new file mode 100644 index 000000000000..d7698bde5de1 --- /dev/null +++ b/toolchain/check/handle_alias.cpp @@ -0,0 +1,75 @@ +// 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/modifiers.h" +#include "toolchain/parse/node_ids.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/typed_insts.h" +#include "toolchain/sem_ir/value_stores.h" + +namespace Carbon::Check { + +auto HandleAliasIntroducer(Context& context, + Parse::AliasIntroducerId /*parse_node*/) -> bool { + context.decl_state_stack().Push(DeclState::Alias); + context.decl_name_stack().PushScopeAndStartName(); + return true; +} + +auto HandleAliasInitializer(Context& /*context*/, + Parse::AliasInitializerId /*parse_node*/) -> bool { + return true; +} + +auto HandleAlias(Context& context, Parse::AliasId /*parse_node*/) -> bool { + auto [expr_node, expr_id] = context.node_stack().PopExprWithParseNode(); + + auto name_context = context.decl_name_stack().FinishName(); + + LimitModifiersOnDecl(context, KeywordModifierSet::Access, + Lex::TokenKind::Alias); + auto modifiers = context.decl_state_stack().innermost().modifier_set; + if (!!(modifiers & KeywordModifierSet::Access)) { + context.TODO(context.decl_state_stack().innermost().saw_access_modifier, + "access modifier"); + } + context.decl_state_stack().Pop(DeclState::Alias); + + auto bind_name_id = context.bind_names().Add( + {.name_id = name_context.name_id_for_new_inst(), + .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst()}); + + auto alias_id = SemIR::InstId::Invalid; + if (expr_id.is_builtin()) { + // Type (`bool`) and value (`false`) literals provided by the builtin + // structure should be turned into name references. + // TODO: Look into handling `false`, this doesn't do it right now because it + // sees a value instruction instead of a builtin. + alias_id = context.AddInst( + {name_context.parse_node, + SemIR::BindAlias{context.insts().Get(expr_id).type_id(), bind_name_id, + expr_id}}); + } else if (auto inst = context.insts().TryGetAs(expr_id)) { + // Pass through name references, albeit changing the name in use. + alias_id = context.AddInst( + {name_context.parse_node, + SemIR::BindAlias{inst->type_id, bind_name_id, inst->value_id}}); + } else { + CARBON_DIAGNOSTIC(AliasRequiresNameRef, Error, + "Alias initializer must be a name reference."); + context.emitter().Emit(expr_node, AliasRequiresNameRef); + alias_id = + context.AddInst({name_context.parse_node, + SemIR::BindAlias{SemIR::TypeId::Error, bind_name_id, + SemIR::InstId::BuiltinError}}); + } + + // Add the name of the binding to the current scope. + context.decl_name_stack().PopScope(); + context.decl_name_stack().AddNameToLookup(name_context, alias_id); + return true; +} + +} // namespace Carbon::Check diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index 068d171e330d..6c60d4344aa4 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -21,6 +21,7 @@ static auto GetImportName(const SemIR::File& import_sem_ir, SemIR::Inst import_inst) -> std::pair { switch (import_inst.kind()) { + case SemIR::InstKind::BindAlias: case SemIR::InstKind::BindName: case SemIR::InstKind::BindSymbolicName: { const auto& bind_name = import_sem_ir.bind_names().Get( diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index ace52473b425..1816a337f4e4 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -73,10 +73,9 @@ class ImportRefResolver { } private: - // Returns the ConstantId for an instruction, or adds it to the stack and + // Returns the ConstantId for an InstId, or adds it to the stack and // returns Invalid if the ConstantId is not ready. - auto GetConstantId(SemIR::TypeId type_id) -> SemIR::ConstantId { - auto inst_id = import_ir_.types().GetInstId(type_id); + auto GetConstantId(SemIR::InstId inst_id) -> SemIR::ConstantId { auto const_id = import_ir_constant_values_.Get(inst_id); if (!const_id.is_valid()) { work_stack_.push_back(inst_id); @@ -84,6 +83,12 @@ class ImportRefResolver { return const_id; } + // Returns the ConstantId for a TypeId, or adds it to the stack and + // returns Invalid if the ConstantId is not ready. + auto GetConstantId(SemIR::TypeId type_id) -> SemIR::ConstantId { + return GetConstantId(import_ir_.types().GetInstId(type_id)); + } + // Tries to resolve the InstId, returning a constant when ready, or Invalid if // more has been added to the stack. A similar API is followed for all // following TryResolveTypedInst helper functions. @@ -109,6 +114,9 @@ class ImportRefResolver { auto inst = import_ir_.insts().Get(inst_id); switch (inst.kind()) { + case SemIR::InstKind::BindAlias: + return TryResolveTypedInst(inst.As()); + case SemIR::InstKind::ConstType: return TryResolveTypedInst(inst.As()); @@ -144,6 +152,14 @@ class ImportRefResolver { } } + auto TryResolveTypedInst(SemIR::BindAlias inst) -> SemIR::ConstantId { + auto value_id = GetConstantId(inst.value_id); + if (!value_id.is_valid()) { + return SemIR::ConstantId::Invalid; + } + return value_id; + } + auto TryResolveTypedInst(SemIR::ConstType inst) -> SemIR::ConstantId { CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType); auto inner_const_id = GetConstantId(inst.inner_id); diff --git a/toolchain/check/testdata/alias/alias_of_alias.carbon b/toolchain/check/testdata/alias/alias_of_alias.carbon new file mode 100644 index 000000000000..a0ff1bdd2412 --- /dev/null +++ b/toolchain/check/testdata/alias/alias_of_alias.carbon @@ -0,0 +1,29 @@ +// 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 + +alias a = bool; +alias b = a; +alias c = b; +let d: c = false; + +// CHECK:STDOUT: --- alias_of_alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a, .b = %b, .c = %c} [template] +// CHECK:STDOUT: %a: type = bind_alias a, bool [template = bool] +// CHECK:STDOUT: %a.ref: type = name_ref a, %a [template = bool] +// CHECK:STDOUT: %b: type = bind_alias b, %a [template = bool] +// CHECK:STDOUT: %b.ref: type = name_ref b, %b [template = bool] +// CHECK:STDOUT: %c: type = bind_alias c, %b [template = bool] +// CHECK:STDOUT: %c.ref: type = name_ref c, %c [template = bool] +// CHECK:STDOUT: %.loc10: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: %d: bool = bind_name d, %.loc10 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/builtins.carbon b/toolchain/check/testdata/alias/builtins.carbon new file mode 100644 index 000000000000..4221c3ecf1da --- /dev/null +++ b/toolchain/check/testdata/alias/builtins.carbon @@ -0,0 +1,31 @@ +// 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 + +alias a = i32; +let a_test: a = 0; + +alias b = bool; +let b_test: b = false; + +// CHECK:STDOUT: --- builtins.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a, .b = %b} [template] +// CHECK:STDOUT: %a: type = bind_alias a, i32 [template = i32] +// CHECK:STDOUT: %a.ref: type = name_ref a, %a [template = i32] +// CHECK:STDOUT: %.loc8: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: %a_test: i32 = bind_name a_test, %.loc8 +// CHECK:STDOUT: %b: type = bind_alias b, bool [template = bool] +// CHECK:STDOUT: %b.ref: type = name_ref b, %b [template = bool] +// CHECK:STDOUT: %.loc11: bool = bool_literal false [template = constants.%.2] +// CHECK:STDOUT: %b_test: bool = bind_name b_test, %.loc11 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_aliased_name_in_diag.carbon b/toolchain/check/testdata/alias/fail_aliased_name_in_diag.carbon new file mode 100644 index 000000000000..93bea1f9862c --- /dev/null +++ b/toolchain/check/testdata/alias/fail_aliased_name_in_diag.carbon @@ -0,0 +1,31 @@ +// 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 + +let a: i32 = 1; +alias b = a; +// CHECK:STDERR: fail_aliased_name_in_diag.carbon:[[@LINE+3]]:8: ERROR: Cannot implicitly convert from `i32` to `type`. +// CHECK:STDERR: let c: b = 2; +// CHECK:STDERR: ^ +let c: b = 2; + +// CHECK:STDOUT: --- fail_aliased_name_in_diag.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.b = %b} [template] +// CHECK:STDOUT: %.loc7: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: %a: i32 = bind_name a, %.loc7 +// CHECK:STDOUT: %a.ref: i32 = name_ref a, %a +// CHECK:STDOUT: %b: i32 = bind_alias b, %a +// CHECK:STDOUT: %b.ref: i32 = name_ref b, %b +// CHECK:STDOUT: %.loc12: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: %c: = bind_name c, +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_bool_value.carbon b/toolchain/check/testdata/alias/fail_bool_value.carbon new file mode 100644 index 000000000000..203f9da1fa4a --- /dev/null +++ b/toolchain/check/testdata/alias/fail_bool_value.carbon @@ -0,0 +1,26 @@ +// 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 + +// CHECK:STDERR: fail_bool_value.carbon:[[@LINE+3]]:11: ERROR: Alias initializer must be a name reference. +// CHECK:STDERR: alias a = false; +// CHECK:STDERR: ^~~~~ +alias a = false; +let a_test: bool = a; + +// CHECK:STDOUT: --- fail_bool_value.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a} [template] +// CHECK:STDOUT: %.loc10: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: %a: = bind_alias a, [template = ] +// CHECK:STDOUT: %a.ref: = name_ref a, %a [template = ] +// CHECK:STDOUT: %a_test: bool = bind_name a_test, +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_control_flow.carbon b/toolchain/check/testdata/alias/fail_control_flow.carbon new file mode 100644 index 000000000000..346233f26847 --- /dev/null +++ b/toolchain/check/testdata/alias/fail_control_flow.carbon @@ -0,0 +1,29 @@ +// 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 + +// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+9]]:11: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: alias a = true or false; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+6]]:11: ERROR: Semantics TODO: `Control flow expressions are currently only supported inside functions.`. +// CHECK:STDERR: alias a = true or false; +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+3]]:11: ERROR: Alias initializer must be a name reference. +// CHECK:STDERR: alias a = true or false; +// CHECK:STDERR: ^~~~~~~~~~~~~ +alias a = true or false; + +// CHECK:STDOUT: --- fail_control_flow.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal true [template] +// CHECK:STDOUT: %.2: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %.loc16: bool = block_arg +// CHECK:STDOUT: %a: = bind_alias a, [template = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_local_in_namespace.carbon b/toolchain/check/testdata/alias/fail_local_in_namespace.carbon new file mode 100644 index 000000000000..3a5758014ab0 --- /dev/null +++ b/toolchain/check/testdata/alias/fail_local_in_namespace.carbon @@ -0,0 +1,43 @@ +// 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 + +namespace NS; + +fn F() -> bool { + // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+6]]:9: ERROR: Name `NS` not found. + // CHECK:STDERR: alias NS.a = false; + // CHECK:STDERR: ^~ + // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+3]]:16: ERROR: Alias initializer must be a name reference. + // CHECK:STDERR: alias NS.a = false; + // CHECK:STDERR: ^~~~~ + alias NS.a = false; + // CHECK:STDERR: fail_local_in_namespace.carbon:[[@LINE+3]]:10: ERROR: Name `a` not found. + // CHECK:STDERR: return NS.a; + // CHECK:STDERR: ^~~~ + return NS.a; +} + +// CHECK:STDOUT: --- fail_local_in_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.NS = %.loc7, .F = %F} [template] +// CHECK:STDOUT: %.loc7: = namespace {} [template] +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc16_16: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: %.loc16_9: = bind_alias , [template = ] +// CHECK:STDOUT: %NS.ref: = name_ref NS, file.%.loc7 [template = file.%.loc7] +// CHECK:STDOUT: %a.ref: = name_ref a, [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_modifiers.carbon b/toolchain/check/testdata/alias/fail_modifiers.carbon new file mode 100644 index 000000000000..945bd06fda70 --- /dev/null +++ b/toolchain/check/testdata/alias/fail_modifiers.carbon @@ -0,0 +1,42 @@ +// 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 + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+21]]:1: ERROR: `abstract` not allowed on `alias` declaration. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+18]]:10: ERROR: `base` not allowed on declaration with `abstract`. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+15]]:1: `abstract` previously appeared here. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+12]]:15: ERROR: `default` not allowed on declaration with `abstract`. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+9]]:1: `abstract` previously appeared here. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+6]]:23: ERROR: `final` not allowed on declaration with `abstract`. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: `abstract` previously appeared here. +// CHECK:STDERR: abstract base default final alias A = i32; +// CHECK:STDERR: ^~~~~~~~ +abstract base default final alias A = i32; + +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:1: ERROR: `impl` not allowed on `alias` declaration. +// CHECK:STDERR: impl alias B = i32; +// CHECK:STDERR: ^~~~ +impl alias B = i32; + +// CHECK:STDOUT: --- fail_modifiers.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.A = %A, .B = %B} [template] +// CHECK:STDOUT: %A: type = bind_alias A, i32 [template = i32] +// CHECK:STDOUT: %B: type = bind_alias B, i32 [template = i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_name_conflict.carbon b/toolchain/check/testdata/alias/fail_name_conflict.carbon new file mode 100644 index 000000000000..dfd1fa9e1eda --- /dev/null +++ b/toolchain/check/testdata/alias/fail_name_conflict.carbon @@ -0,0 +1,50 @@ +// 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 + +alias a = i32; +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE+6]]:5: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: var a: i32 = 1; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE-4]]:7: Name is previously declared here. +// CHECK:STDERR: alias a = i32; +// CHECK:STDERR: ^ +var a: i32 = 1; + +var b: i32 = 2; +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE+6]]:7: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: alias b = i32; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_name_conflict.carbon:[[@LINE-4]]:5: Name is previously declared here. +// CHECK:STDERR: var b: i32 = 2; +// CHECK:STDERR: ^ +alias b = i32; + +// CHECK:STDOUT: --- fail_name_conflict.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.2: i32 = int_literal 2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a.loc7, .b = %b} [template] +// CHECK:STDOUT: %a.loc7: type = bind_alias a, i32 [template = i32] +// CHECK:STDOUT: %a.var: ref i32 = var a +// CHECK:STDOUT: %a.loc14: ref i32 = bind_name a, %a.var +// CHECK:STDOUT: %b.var: ref i32 = var b +// CHECK:STDOUT: %b: ref i32 = bind_name b, %b.var +// CHECK:STDOUT: %.loc23: type = bind_alias , i32 [template = i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc14: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: assign file.%a.var, %.loc14 +// CHECK:STDOUT: %.loc16: i32 = int_literal 2 [template = constants.%.2] +// CHECK:STDOUT: assign file.%b.var, %.loc16 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_not_constant.carbon b/toolchain/check/testdata/alias/fail_not_constant.carbon new file mode 100644 index 000000000000..f7ac37b191ab --- /dev/null +++ b/toolchain/check/testdata/alias/fail_not_constant.carbon @@ -0,0 +1,46 @@ +// 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 + +fn F() { + var a: i32 = 0; + var b: i32* = &a; + // CHECK:STDERR: fail_not_constant.carbon:[[@LINE+3]]:13: ERROR: Alias initializer must be a name reference. + // CHECK:STDERR: alias c = *b; + // CHECK:STDERR: ^~ + alias c = *b; +} + +// CHECK:STDOUT: --- fail_not_constant.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: %.2: type = ptr_type i32 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} [template] +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref i32 = var a +// CHECK:STDOUT: %a: ref i32 = bind_name a, %a.var +// CHECK:STDOUT: %.loc8: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: assign %a.var, %.loc8 +// CHECK:STDOUT: %.loc9_13: type = ptr_type i32 [template = constants.%.2] +// CHECK:STDOUT: %b.var: ref i32* = var b +// CHECK:STDOUT: %b: ref i32* = bind_name b, %b.var +// CHECK:STDOUT: %a.ref: ref i32 = name_ref a, %a +// CHECK:STDOUT: %.loc9_17: i32* = addr_of %a.ref +// CHECK:STDOUT: assign %b.var, %.loc9_17 +// CHECK:STDOUT: %b.ref: ref i32* = name_ref b, %b +// CHECK:STDOUT: %.loc13_14: i32* = bind_value %b.ref +// CHECK:STDOUT: %.loc13_13: ref i32 = deref %.loc13_14 +// CHECK:STDOUT: %c: = bind_alias c, [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/fail_todo_private.carbon b/toolchain/check/testdata/alias/fail_todo_private.carbon new file mode 100644 index 000000000000..635e9d197cd4 --- /dev/null +++ b/toolchain/check/testdata/alias/fail_todo_private.carbon @@ -0,0 +1,18 @@ +// 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 + +// CHECK:STDERR: fail_todo_private.carbon:[[@LINE+3]]:1: ERROR: Semantics TODO: `access modifier`. +// CHECK:STDERR: private alias A = i32; +// CHECK:STDERR: ^~~~~~~ +private alias A = i32; + +// CHECK:STDOUT: --- fail_todo_private.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.A = %A} [template] +// CHECK:STDOUT: %A: type = bind_alias A, i32 [template = i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/import.carbon b/toolchain/check/testdata/alias/import.carbon new file mode 100644 index 000000000000..88d9dc29ef82 --- /dev/null +++ b/toolchain/check/testdata/alias/import.carbon @@ -0,0 +1,61 @@ +// 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 + +// --- a.carbon + +library "a" api; + +var a: i32 = 0; + +alias b = a; + +// --- b.carbon + +library "b" api; + +import library "a"; + +var c: i32 = b; + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a, .b = %b} [template] +// CHECK:STDOUT: %a.var: ref i32 = var a +// CHECK:STDOUT: %a: ref i32 = bind_name a, %a.var +// CHECK:STDOUT: %a.ref: ref i32 = name_ref a, %a +// CHECK:STDOUT: %b: ref i32 = bind_alias b, %a +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc4: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: assign file.%a.var, %.loc4 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %import_ref.1, .b = %import_ref.2, .c = %c} [template] +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+2, unused +// CHECK:STDOUT: %import_ref.2: ref i32 = import_ref ir1, inst+7, used +// CHECK:STDOUT: %c.var: ref i32 = var c +// CHECK:STDOUT: %c: ref i32 = bind_name c, %c.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: ref i32 = name_ref b, file.%import_ref.2 +// CHECK:STDOUT: %.loc6: i32 = bind_value %b.ref +// CHECK:STDOUT: assign file.%c.var, %.loc6 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/import_order.carbon b/toolchain/check/testdata/alias/import_order.carbon new file mode 100644 index 000000000000..590a7a20c53d --- /dev/null +++ b/toolchain/check/testdata/alias/import_order.carbon @@ -0,0 +1,88 @@ +// 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 + +// --- a.carbon + +library "a" api; + +alias a = i32; +alias b = a; +alias c = b; +alias d = c; + +// --- b.carbon + +library "b" api; + +import library "a"; + +// Access imports in reverse order of export. +var d_val: d = 1; +var c_val: c = d_val; +var b_val: b = c_val; +var a_val: a = b_val; +var i32_val: i32 = a_val; + +// CHECK:STDOUT: --- a.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %a, .b = %b, .c = %c, .d = %d} [template] +// CHECK:STDOUT: %a: type = bind_alias a, i32 [template = i32] +// CHECK:STDOUT: %a.ref: type = name_ref a, %a [template = i32] +// CHECK:STDOUT: %b: type = bind_alias b, %a [template = i32] +// CHECK:STDOUT: %b.ref: type = name_ref b, %b [template = i32] +// CHECK:STDOUT: %c: type = bind_alias c, %b [template = i32] +// CHECK:STDOUT: %c.ref: type = name_ref c, %c [template = i32] +// CHECK:STDOUT: %d: type = bind_alias d, %c [template = i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.a = %import_ref.1, .b = %import_ref.2, .c = %import_ref.3, .d = %import_ref.4, .d_val = %d_val, .c_val = %c_val, .b_val = %b_val, .a_val = %a_val, .i32_val = %i32_val} [template] +// CHECK:STDOUT: %import_ref.1: type = import_ref ir1, inst+1, used [template = i32] +// CHECK:STDOUT: %import_ref.2: type = import_ref ir1, inst+3, used [template = i32] +// CHECK:STDOUT: %import_ref.3: type = import_ref ir1, inst+5, used [template = i32] +// CHECK:STDOUT: %import_ref.4: type = import_ref ir1, inst+7, used [template = i32] +// CHECK:STDOUT: %d.ref: type = name_ref d, %import_ref.4 [template = i32] +// CHECK:STDOUT: %d_val.var: ref i32 = var d_val +// CHECK:STDOUT: %d_val: ref i32 = bind_name d_val, %d_val.var +// CHECK:STDOUT: %c.ref: type = name_ref c, %import_ref.3 [template = i32] +// CHECK:STDOUT: %c_val.var: ref i32 = var c_val +// CHECK:STDOUT: %c_val: ref i32 = bind_name c_val, %c_val.var +// CHECK:STDOUT: %b.ref: type = name_ref b, %import_ref.2 [template = i32] +// CHECK:STDOUT: %b_val.var: ref i32 = var b_val +// CHECK:STDOUT: %b_val: ref i32 = bind_name b_val, %b_val.var +// CHECK:STDOUT: %a.ref: type = name_ref a, %import_ref.1 [template = i32] +// CHECK:STDOUT: %a_val.var: ref i32 = var a_val +// CHECK:STDOUT: %a_val: ref i32 = bind_name a_val, %a_val.var +// CHECK:STDOUT: %i32_val.var: ref i32 = var i32_val +// CHECK:STDOUT: %i32_val: ref i32 = bind_name i32_val, %i32_val.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: i32 = int_literal 1 [template = constants.%.1] +// CHECK:STDOUT: assign file.%d_val.var, %.loc7 +// CHECK:STDOUT: %d_val.ref: ref i32 = name_ref d_val, file.%d_val +// CHECK:STDOUT: %.loc8: i32 = bind_value %d_val.ref +// CHECK:STDOUT: assign file.%c_val.var, %.loc8 +// CHECK:STDOUT: %c_val.ref: ref i32 = name_ref c_val, file.%c_val +// CHECK:STDOUT: %.loc9: i32 = bind_value %c_val.ref +// CHECK:STDOUT: assign file.%b_val.var, %.loc9 +// CHECK:STDOUT: %b_val.ref: ref i32 = name_ref b_val, file.%b_val +// CHECK:STDOUT: %.loc10: i32 = bind_value %b_val.ref +// CHECK:STDOUT: assign file.%a_val.var, %.loc10 +// CHECK:STDOUT: %a_val.ref: ref i32 = name_ref a_val, file.%a_val +// CHECK:STDOUT: %.loc11: i32 = bind_value %a_val.ref +// CHECK:STDOUT: assign file.%i32_val.var, %.loc11 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/in_namespace.carbon b/toolchain/check/testdata/alias/in_namespace.carbon new file mode 100644 index 000000000000..8295b1ddffd5 --- /dev/null +++ b/toolchain/check/testdata/alias/in_namespace.carbon @@ -0,0 +1,38 @@ +// 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 + +namespace NS; +alias NS.a = bool; + +let b: NS.a = false; + +fn F() -> NS.a { + return false; +} + +// CHECK:STDOUT: --- in_namespace.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: bool = bool_literal false [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.NS = %.loc7, .F = %F} [template] +// CHECK:STDOUT: %.loc7: = namespace {.a = %a} [template] +// CHECK:STDOUT: %a: type = bind_alias a, bool [template = bool] +// CHECK:STDOUT: %NS.ref: = name_ref NS, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %a.ref: type = name_ref a, %a [template = bool] +// CHECK:STDOUT: %.loc10: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: %b: bool = bind_name b, %.loc10 +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> bool { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc13: bool = bool_literal false [template = constants.%.1] +// CHECK:STDOUT: return %.loc13 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/alias/local.carbon b/toolchain/check/testdata/alias/local.carbon new file mode 100644 index 000000000000..2b42e4125608 --- /dev/null +++ b/toolchain/check/testdata/alias/local.carbon @@ -0,0 +1,36 @@ +// 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 + +fn F() -> i32 { + var a: i32 = 0; + alias b = a; + return b; +} + +// CHECK:STDOUT: --- local.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} [template] +// CHECK:STDOUT: %F: = fn_decl @F [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.var: ref i32 = var a +// CHECK:STDOUT: %a: ref i32 = bind_name a, %a.var +// CHECK:STDOUT: %.loc8: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: assign %a.var, %.loc8 +// CHECK:STDOUT: %a.ref: ref i32 = name_ref a, %a +// CHECK:STDOUT: %b: ref i32 = bind_alias b, %a +// CHECK:STDOUT: %b.ref: ref i32 = name_ref b, %b +// CHECK:STDOUT: %.loc10: i32 = bind_value %b.ref +// CHECK:STDOUT: return %.loc10 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_method.carbon b/toolchain/check/testdata/class/fail_method.carbon index 8fa9f185d42e..e168484e0d5b 100644 --- a/toolchain/check/testdata/class/fail_method.carbon +++ b/toolchain/check/testdata/class/fail_method.carbon @@ -9,6 +9,8 @@ class Class { fn WithSelf[self: Class](); } +alias A = Class.WithSelf; + fn F(c: Class) { c.NoSelf(); c.WithSelf(); @@ -17,17 +19,25 @@ fn F(c: Class) { // CHECK:STDERR: fail_method.carbon:[[@LINE+6]]:3: ERROR: Missing object argument in method call. // CHECK:STDERR: Class.WithSelf(); // CHECK:STDERR: ^~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_method.carbon:[[@LINE-11]]:3: Calling function declared here. + // CHECK:STDERR: fail_method.carbon:[[@LINE-13]]:3: Calling function declared here. // CHECK:STDERR: fn WithSelf[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Class.WithSelf(); // CHECK:STDERR: fail_method.carbon:[[@LINE+6]]:3: ERROR: 1 argument(s) passed to function expecting 0 argument(s). // CHECK:STDERR: Class.WithSelf(c); // CHECK:STDERR: ^~~~~~~~~~~~~~~ - // CHECK:STDERR: fail_method.carbon:[[@LINE-18]]:3: Calling function declared here. + // CHECK:STDERR: fail_method.carbon:[[@LINE-20]]:3: Calling function declared here. // CHECK:STDERR: fn WithSelf[self: Class](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Class.WithSelf(c); + + // CHECK:STDERR: fail_method.carbon:[[@LINE+6]]:3: ERROR: Missing object argument in method call. + // CHECK:STDERR: A(); + // CHECK:STDERR: ^~ + // CHECK:STDERR: fail_method.carbon:[[@LINE-28]]:3: Calling function declared here. + // CHECK:STDERR: fn WithSelf[self: Class](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ + A(); } // CHECK:STDOUT: --- fail_method.carbon @@ -40,8 +50,11 @@ fn F(c: Class) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace {.Class = %Class.decl, .F = %F} [template] +// CHECK:STDOUT: package: = namespace {.Class = %Class.decl, .A = %A, .F = %F} [template] // CHECK:STDOUT: %Class.decl = class_decl @Class, () +// CHECK:STDOUT: %Class.ref: type = name_ref Class, constants.%Class [template = constants.%Class] +// CHECK:STDOUT: %WithSelf.ref: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] +// CHECK:STDOUT: %A: = bind_alias A, @Class.%WithSelf [template = @Class.%WithSelf] // CHECK:STDOUT: %F: = fn_decl @F [template] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -60,22 +73,24 @@ fn F(c: Class) { // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%c: Class) { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %c.ref.loc13: Class = name_ref c, %c -// CHECK:STDOUT: %NoSelf.ref.loc13: = name_ref NoSelf, @Class.%NoSelf [template = @Class.%NoSelf] -// CHECK:STDOUT: %.loc13: init () = call %NoSelf.ref.loc13() -// CHECK:STDOUT: %c.ref.loc14: Class = name_ref c, %c -// CHECK:STDOUT: %.loc14_4: = bound_method %c.ref.loc14, @Class.%WithSelf -// CHECK:STDOUT: %.loc14_13: init () = call %.loc14_4(%c.ref.loc14) -// CHECK:STDOUT: %Class.ref.loc16: type = name_ref Class, constants.%Class [template = constants.%Class] -// CHECK:STDOUT: %NoSelf.ref.loc16: = name_ref NoSelf, @Class.%NoSelf [template = @Class.%NoSelf] -// CHECK:STDOUT: %.loc16: init () = call %NoSelf.ref.loc16() -// CHECK:STDOUT: %Class.ref.loc23: type = name_ref Class, constants.%Class [template = constants.%Class] -// CHECK:STDOUT: %WithSelf.ref.loc23: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] -// CHECK:STDOUT: %.loc23: init () = call %WithSelf.ref.loc23() -// CHECK:STDOUT: %Class.ref.loc30: type = name_ref Class, constants.%Class [template = constants.%Class] -// CHECK:STDOUT: %WithSelf.ref.loc30: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] -// CHECK:STDOUT: %c.ref.loc30: Class = name_ref c, %c -// CHECK:STDOUT: %.loc30: init () = call %WithSelf.ref.loc30() +// CHECK:STDOUT: %c.ref.loc15: Class = name_ref c, %c +// CHECK:STDOUT: %NoSelf.ref.loc15: = name_ref NoSelf, @Class.%NoSelf [template = @Class.%NoSelf] +// CHECK:STDOUT: %.loc15: init () = call %NoSelf.ref.loc15() +// CHECK:STDOUT: %c.ref.loc16: Class = name_ref c, %c +// CHECK:STDOUT: %.loc16_4: = bound_method %c.ref.loc16, @Class.%WithSelf +// CHECK:STDOUT: %.loc16_13: init () = call %.loc16_4(%c.ref.loc16) +// CHECK:STDOUT: %Class.ref.loc18: type = name_ref Class, constants.%Class [template = constants.%Class] +// CHECK:STDOUT: %NoSelf.ref.loc18: = name_ref NoSelf, @Class.%NoSelf [template = @Class.%NoSelf] +// CHECK:STDOUT: %.loc18: init () = call %NoSelf.ref.loc18() +// CHECK:STDOUT: %Class.ref.loc25: type = name_ref Class, constants.%Class [template = constants.%Class] +// CHECK:STDOUT: %WithSelf.ref.loc25: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] +// CHECK:STDOUT: %.loc25: init () = call %WithSelf.ref.loc25() +// CHECK:STDOUT: %Class.ref.loc32: type = name_ref Class, constants.%Class [template = constants.%Class] +// CHECK:STDOUT: %WithSelf.ref.loc32: = name_ref WithSelf, @Class.%WithSelf [template = @Class.%WithSelf] +// CHECK:STDOUT: %c.ref.loc32: Class = name_ref c, %c +// CHECK:STDOUT: %.loc32: init () = call %WithSelf.ref.loc32() +// CHECK:STDOUT: %A.ref: = name_ref A, file.%A [template = @Class.%WithSelf] +// CHECK:STDOUT: %.loc40: init () = call %A.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/method.carbon b/toolchain/check/testdata/class/method.carbon index 45781d19d83d..20f9e442290e 100644 --- a/toolchain/check/testdata/class/method.carbon +++ b/toolchain/check/testdata/class/method.carbon @@ -8,6 +8,8 @@ class Class { fn F[self: Class]() -> i32; fn G[addr self: Class*]() -> i32; + alias A = F; + var k: i32; } @@ -21,6 +23,10 @@ fn Call(c: Class) -> i32 { return c.F(); } +fn CallAlias(c: Class) -> i32 { + return c.A(); +} + fn CallOnConstBoundMethod() -> i32 { return ({.k = 1} as Class).F(); } @@ -61,10 +67,11 @@ fn CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace {.Class = %Class.decl, .Call = %Call, .CallOnConstBoundMethod = %CallOnConstBoundMethod, .CallWithAddr = %CallWithAddr, .CallFThroughPointer = %CallFThroughPointer, .CallGThroughPointer = %CallGThroughPointer, .Make = %Make, .CallFOnInitializingExpr = %CallFOnInitializingExpr, .CallGOnInitializingExpr = %CallGOnInitializingExpr} [template] +// CHECK:STDOUT: package: = namespace {.Class = %Class.decl, .Call = %Call, .CallAlias = %CallAlias, .CallOnConstBoundMethod = %CallOnConstBoundMethod, .CallWithAddr = %CallWithAddr, .CallFThroughPointer = %CallFThroughPointer, .CallGThroughPointer = %CallGThroughPointer, .Make = %Make, .CallFOnInitializingExpr = %CallFOnInitializingExpr, .CallGOnInitializingExpr = %CallGOnInitializingExpr} [template] // CHECK:STDOUT: %Class.decl = class_decl @Class, () // CHECK:STDOUT: %F: = fn_decl @F [template] // CHECK:STDOUT: %Call: = fn_decl @Call [template] +// CHECK:STDOUT: %CallAlias: = fn_decl @CallAlias [template] // CHECK:STDOUT: %CallOnConstBoundMethod: = fn_decl @CallOnConstBoundMethod [template] // CHECK:STDOUT: %CallWithAddr: = fn_decl @CallWithAddr [template] // CHECK:STDOUT: %CallFThroughPointer: = fn_decl @CallFThroughPointer [template] @@ -77,20 +84,23 @@ fn CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: class @Class { // CHECK:STDOUT: %F: = fn_decl @F [template] // CHECK:STDOUT: %G: = fn_decl @G [template] -// CHECK:STDOUT: %.loc11: = field_decl k, element0 [template] +// CHECK:STDOUT: %F.ref: = name_ref F, %F [template = %F] +// CHECK:STDOUT: %A: = bind_alias A, %F [template = %F] +// CHECK:STDOUT: %.loc13: = field_decl k, element0 [template] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F // CHECK:STDOUT: .G = %G -// CHECK:STDOUT: .k = %.loc11 +// CHECK:STDOUT: .A = %A +// CHECK:STDOUT: .k = %.loc13 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F[%self: Class]() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: Class = name_ref self, %self -// CHECK:STDOUT: %.loc15_14.1: ref i32 = class_element_access %self.ref, element0 -// CHECK:STDOUT: %.loc15_14.2: i32 = bind_value %.loc15_14.1 -// CHECK:STDOUT: return %.loc15_14.2 +// CHECK:STDOUT: %.loc17_14.1: ref i32 = class_element_access %self.ref, element0 +// CHECK:STDOUT: %.loc17_14.2: i32 = bind_value %.loc17_14.1 +// CHECK:STDOUT: return %.loc17_14.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @G[addr %self: Class*]() -> i32; @@ -98,30 +108,40 @@ fn CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: fn @Call(%c: Class) -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %c.ref: Class = name_ref c, %c -// CHECK:STDOUT: %.loc21_11: = bound_method %c.ref, @Class.%F -// CHECK:STDOUT: %.loc21_13.1: init i32 = call %.loc21_11(%c.ref) -// CHECK:STDOUT: %.loc21_15: i32 = value_of_initializer %.loc21_13.1 -// CHECK:STDOUT: %.loc21_13.2: i32 = converted %.loc21_13.1, %.loc21_15 -// CHECK:STDOUT: return %.loc21_13.2 +// CHECK:STDOUT: %.loc23_11: = bound_method %c.ref, @Class.%F +// CHECK:STDOUT: %.loc23_13.1: init i32 = call %.loc23_11(%c.ref) +// CHECK:STDOUT: %.loc23_15: i32 = value_of_initializer %.loc23_13.1 +// CHECK:STDOUT: %.loc23_13.2: i32 = converted %.loc23_13.1, %.loc23_15 +// CHECK:STDOUT: return %.loc23_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @CallAlias(%c: Class) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: Class = name_ref c, %c +// CHECK:STDOUT: %.loc27_11: = bound_method %c.ref, @Class.%A +// CHECK:STDOUT: %.loc27_13.1: init i32 = call %.loc27_11(%c.ref) +// CHECK:STDOUT: %.loc27_15: i32 = value_of_initializer %.loc27_13.1 +// CHECK:STDOUT: %.loc27_13.2: i32 = converted %.loc27_13.1, %.loc27_15 +// CHECK:STDOUT: return %.loc27_13.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallOnConstBoundMethod() -> i32 { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc25_17: i32 = int_literal 1 [template = constants.%.5] -// CHECK:STDOUT: %.loc25_18.1: {.k: i32} = struct_literal (%.loc25_17) +// CHECK:STDOUT: %.loc31_17: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %.loc31_18.1: {.k: i32} = struct_literal (%.loc31_17) // CHECK:STDOUT: %Class.ref: type = name_ref Class, constants.%Class [template = constants.%Class] -// CHECK:STDOUT: %.loc25_18.2: ref Class = temporary_storage -// CHECK:STDOUT: %.loc25_18.3: ref i32 = class_element_access %.loc25_18.2, element0 -// CHECK:STDOUT: %.loc25_18.4: init i32 = initialize_from %.loc25_17 to %.loc25_18.3 [template = constants.%.5] -// CHECK:STDOUT: %.loc25_18.5: init Class = class_init (%.loc25_18.4), %.loc25_18.2 [template = constants.%.6] -// CHECK:STDOUT: %.loc25_18.6: ref Class = temporary %.loc25_18.2, %.loc25_18.5 -// CHECK:STDOUT: %.loc25_18.7: ref Class = converted %.loc25_18.1, %.loc25_18.6 -// CHECK:STDOUT: %.loc25_29: = bound_method %.loc25_18.7, @Class.%F -// CHECK:STDOUT: %.loc25_18.8: Class = bind_value %.loc25_18.7 -// CHECK:STDOUT: %.loc25_31.1: init i32 = call %.loc25_29(%.loc25_18.8) -// CHECK:STDOUT: %.loc25_33: i32 = value_of_initializer %.loc25_31.1 -// CHECK:STDOUT: %.loc25_31.2: i32 = converted %.loc25_31.1, %.loc25_33 -// CHECK:STDOUT: return %.loc25_31.2 +// CHECK:STDOUT: %.loc31_18.2: ref Class = temporary_storage +// CHECK:STDOUT: %.loc31_18.3: ref i32 = class_element_access %.loc31_18.2, element0 +// CHECK:STDOUT: %.loc31_18.4: init i32 = initialize_from %.loc31_17 to %.loc31_18.3 [template = constants.%.5] +// CHECK:STDOUT: %.loc31_18.5: init Class = class_init (%.loc31_18.4), %.loc31_18.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc31_18.6: ref Class = temporary %.loc31_18.2, %.loc31_18.5 +// CHECK:STDOUT: %.loc31_18.7: ref Class = converted %.loc31_18.1, %.loc31_18.6 +// CHECK:STDOUT: %.loc31_29: = bound_method %.loc31_18.7, @Class.%F +// CHECK:STDOUT: %.loc31_18.8: Class = bind_value %.loc31_18.7 +// CHECK:STDOUT: %.loc31_31.1: init i32 = call %.loc31_29(%.loc31_18.8) +// CHECK:STDOUT: %.loc31_33: i32 = value_of_initializer %.loc31_31.1 +// CHECK:STDOUT: %.loc31_31.2: i32 = converted %.loc31_31.1, %.loc31_33 +// CHECK:STDOUT: return %.loc31_31.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallWithAddr() -> i32 { @@ -130,36 +150,36 @@ fn CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: %c.var: ref Class = var c // CHECK:STDOUT: %c: ref Class = bind_name c, %c.var // CHECK:STDOUT: %c.ref: ref Class = name_ref c, %c -// CHECK:STDOUT: %.loc30_11: = bound_method %c.ref, @Class.%G -// CHECK:STDOUT: %.loc30_10: Class* = addr_of %c.ref -// CHECK:STDOUT: %.loc30_13.1: init i32 = call %.loc30_11(%.loc30_10) -// CHECK:STDOUT: %.loc30_15: i32 = value_of_initializer %.loc30_13.1 -// CHECK:STDOUT: %.loc30_13.2: i32 = converted %.loc30_13.1, %.loc30_15 -// CHECK:STDOUT: return %.loc30_13.2 +// CHECK:STDOUT: %.loc36_11: = bound_method %c.ref, @Class.%G +// CHECK:STDOUT: %.loc36_10: Class* = addr_of %c.ref +// CHECK:STDOUT: %.loc36_13.1: init i32 = call %.loc36_11(%.loc36_10) +// CHECK:STDOUT: %.loc36_15: i32 = value_of_initializer %.loc36_13.1 +// CHECK:STDOUT: %.loc36_13.2: i32 = converted %.loc36_13.1, %.loc36_15 +// CHECK:STDOUT: return %.loc36_13.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallFThroughPointer(%p: Class*) -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: Class* = name_ref p, %p -// CHECK:STDOUT: %.loc34_11.1: ref Class = deref %p.ref -// CHECK:STDOUT: %.loc34_14: = bound_method %.loc34_11.1, @Class.%F -// CHECK:STDOUT: %.loc34_11.2: Class = bind_value %.loc34_11.1 -// CHECK:STDOUT: %.loc34_16.1: init i32 = call %.loc34_14(%.loc34_11.2) -// CHECK:STDOUT: %.loc34_18: i32 = value_of_initializer %.loc34_16.1 -// CHECK:STDOUT: %.loc34_16.2: i32 = converted %.loc34_16.1, %.loc34_18 -// CHECK:STDOUT: return %.loc34_16.2 +// CHECK:STDOUT: %.loc40_11.1: ref Class = deref %p.ref +// CHECK:STDOUT: %.loc40_14: = bound_method %.loc40_11.1, @Class.%F +// CHECK:STDOUT: %.loc40_11.2: Class = bind_value %.loc40_11.1 +// CHECK:STDOUT: %.loc40_16.1: init i32 = call %.loc40_14(%.loc40_11.2) +// CHECK:STDOUT: %.loc40_18: i32 = value_of_initializer %.loc40_16.1 +// CHECK:STDOUT: %.loc40_16.2: i32 = converted %.loc40_16.1, %.loc40_18 +// CHECK:STDOUT: return %.loc40_16.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGThroughPointer(%p: Class*) -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %p.ref: Class* = name_ref p, %p -// CHECK:STDOUT: %.loc38_11.1: ref Class = deref %p.ref -// CHECK:STDOUT: %.loc38_14: = bound_method %.loc38_11.1, @Class.%G -// CHECK:STDOUT: %.loc38_11.2: Class* = addr_of %.loc38_11.1 -// CHECK:STDOUT: %.loc38_16.1: init i32 = call %.loc38_14(%.loc38_11.2) -// CHECK:STDOUT: %.loc38_18: i32 = value_of_initializer %.loc38_16.1 -// CHECK:STDOUT: %.loc38_16.2: i32 = converted %.loc38_16.1, %.loc38_18 -// CHECK:STDOUT: return %.loc38_16.2 +// CHECK:STDOUT: %.loc44_11.1: ref Class = deref %p.ref +// CHECK:STDOUT: %.loc44_14: = bound_method %.loc44_11.1, @Class.%G +// CHECK:STDOUT: %.loc44_11.2: Class* = addr_of %.loc44_11.1 +// CHECK:STDOUT: %.loc44_16.1: init i32 = call %.loc44_14(%.loc44_11.2) +// CHECK:STDOUT: %.loc44_18: i32 = value_of_initializer %.loc44_16.1 +// CHECK:STDOUT: %.loc44_16.2: i32 = converted %.loc44_16.1, %.loc44_18 +// CHECK:STDOUT: return %.loc44_16.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> %return: Class; @@ -167,28 +187,28 @@ fn CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: fn @CallFOnInitializingExpr() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: = name_ref Make, file.%Make [template = file.%Make] -// CHECK:STDOUT: %.loc44_14.1: ref Class = temporary_storage -// CHECK:STDOUT: %.loc44_14.2: init Class = call %Make.ref() to %.loc44_14.1 -// CHECK:STDOUT: %.loc44_14.3: ref Class = temporary %.loc44_14.1, %.loc44_14.2 -// CHECK:STDOUT: %.loc44_16: = bound_method %.loc44_14.3, @Class.%F -// CHECK:STDOUT: %.loc44_14.4: Class = bind_value %.loc44_14.3 -// CHECK:STDOUT: %.loc44_18.1: init i32 = call %.loc44_16(%.loc44_14.4) -// CHECK:STDOUT: %.loc44_20: i32 = value_of_initializer %.loc44_18.1 -// CHECK:STDOUT: %.loc44_18.2: i32 = converted %.loc44_18.1, %.loc44_20 -// CHECK:STDOUT: return %.loc44_18.2 +// CHECK:STDOUT: %.loc50_14.1: ref Class = temporary_storage +// CHECK:STDOUT: %.loc50_14.2: init Class = call %Make.ref() to %.loc50_14.1 +// CHECK:STDOUT: %.loc50_14.3: ref Class = temporary %.loc50_14.1, %.loc50_14.2 +// CHECK:STDOUT: %.loc50_16: = bound_method %.loc50_14.3, @Class.%F +// CHECK:STDOUT: %.loc50_14.4: Class = bind_value %.loc50_14.3 +// CHECK:STDOUT: %.loc50_18.1: init i32 = call %.loc50_16(%.loc50_14.4) +// CHECK:STDOUT: %.loc50_20: i32 = value_of_initializer %.loc50_18.1 +// CHECK:STDOUT: %.loc50_18.2: i32 = converted %.loc50_18.1, %.loc50_20 +// CHECK:STDOUT: return %.loc50_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @CallGOnInitializingExpr() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Make.ref: = name_ref Make, file.%Make [template = file.%Make] -// CHECK:STDOUT: %.loc48_14.1: ref Class = temporary_storage -// CHECK:STDOUT: %.loc48_14.2: init Class = call %Make.ref() to %.loc48_14.1 -// CHECK:STDOUT: %.loc48_14.3: ref Class = temporary %.loc48_14.1, %.loc48_14.2 -// CHECK:STDOUT: %.loc48_16: = bound_method %.loc48_14.3, @Class.%G -// CHECK:STDOUT: %.loc48_14.4: Class* = addr_of %.loc48_14.3 -// CHECK:STDOUT: %.loc48_18.1: init i32 = call %.loc48_16(%.loc48_14.4) -// CHECK:STDOUT: %.loc48_20: i32 = value_of_initializer %.loc48_18.1 -// CHECK:STDOUT: %.loc48_18.2: i32 = converted %.loc48_18.1, %.loc48_20 -// CHECK:STDOUT: return %.loc48_18.2 +// CHECK:STDOUT: %.loc54_14.1: ref Class = temporary_storage +// CHECK:STDOUT: %.loc54_14.2: init Class = call %Make.ref() to %.loc54_14.1 +// CHECK:STDOUT: %.loc54_14.3: ref Class = temporary %.loc54_14.1, %.loc54_14.2 +// CHECK:STDOUT: %.loc54_16: = bound_method %.loc54_14.3, @Class.%G +// CHECK:STDOUT: %.loc54_14.4: Class* = addr_of %.loc54_14.3 +// CHECK:STDOUT: %.loc54_18.1: init i32 = call %.loc54_16(%.loc54_14.4) +// CHECK:STDOUT: %.loc54_20: i32 = value_of_initializer %.loc54_18.1 +// CHECK:STDOUT: %.loc54_18.2: i32 = converted %.loc54_18.1, %.loc54_20 +// CHECK:STDOUT: return %.loc54_18.2 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/call/alias.carbon b/toolchain/check/testdata/function/call/alias.carbon new file mode 100644 index 000000000000..065f5b0d113d --- /dev/null +++ b/toolchain/check/testdata/function/call/alias.carbon @@ -0,0 +1,44 @@ +// 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 + +fn A() -> i32 { return 0; } + +alias B = A; + +fn Main() { + var b: i32 = B(); +} + +// CHECK:STDOUT: --- alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.A = %A, .B = %B, .Main = %Main} [template] +// CHECK:STDOUT: %A: = fn_decl @A [template] +// CHECK:STDOUT: %A.ref: = name_ref A, %A [template = %A] +// CHECK:STDOUT: %B: = bind_alias B, %A [template = %A] +// CHECK:STDOUT: %Main: = fn_decl @Main [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: return %.loc7 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Main() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.var: ref i32 = var b +// CHECK:STDOUT: %b: ref i32 = bind_name b, %b.var +// CHECK:STDOUT: %B.ref: = name_ref B, file.%B [template = file.%A] +// CHECK:STDOUT: %.loc12: init i32 = call %B.ref() +// CHECK:STDOUT: assign %b.var, %.loc12 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/alias.carbon b/toolchain/check/testdata/namespace/alias.carbon new file mode 100644 index 000000000000..4dcc43fdbd0d --- /dev/null +++ b/toolchain/check/testdata/namespace/alias.carbon @@ -0,0 +1,62 @@ +// 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 + +namespace NS; + +alias ns = NS; + +fn NS.A() -> i32 { return 0; } + +fn B() -> i32 { return ns.A(); } + +alias C = NS.A; + +fn D() -> i32 { return C(); } + +// CHECK:STDOUT: --- alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.NS = %.loc7, .ns = %ns, .B = %B, .C = %C, .D = %D} [template] +// CHECK:STDOUT: %.loc7: = namespace {.A = %A} [template] +// CHECK:STDOUT: %NS.ref.loc9: = name_ref NS, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %ns: = bind_alias ns, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %A: = fn_decl @A [template] +// CHECK:STDOUT: %B: = fn_decl @B [template] +// CHECK:STDOUT: %NS.ref.loc15: = name_ref NS, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %A.ref: = name_ref A, %A [template = %A] +// CHECK:STDOUT: %C: = bind_alias C, %A [template = %A] +// CHECK:STDOUT: %D: = fn_decl @D [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc11: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: return %.loc11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %ns.ref: = name_ref ns, file.%ns [template = file.%.loc7] +// CHECK:STDOUT: %A.ref: = name_ref A, file.%A [template = file.%A] +// CHECK:STDOUT: %.loc13_28.1: init i32 = call %A.ref() +// CHECK:STDOUT: %.loc13_30: i32 = value_of_initializer %.loc13_28.1 +// CHECK:STDOUT: %.loc13_28.2: i32 = converted %.loc13_28.1, %.loc13_30 +// CHECK:STDOUT: return %.loc13_28.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.ref: = name_ref C, file.%C [template = file.%A] +// CHECK:STDOUT: %.loc17_25.1: init i32 = call %C.ref() +// CHECK:STDOUT: %.loc17_27: i32 = value_of_initializer %.loc17_25.1 +// CHECK:STDOUT: %.loc17_25.2: i32 = converted %.loc17_25.1, %.loc17_27 +// CHECK:STDOUT: return %.loc17_25.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/fail_decl_in_alias.carbon b/toolchain/check/testdata/namespace/fail_decl_in_alias.carbon new file mode 100644 index 000000000000..9adbf6b52208 --- /dev/null +++ b/toolchain/check/testdata/namespace/fail_decl_in_alias.carbon @@ -0,0 +1,39 @@ +// 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 + +namespace NS; + +alias ns = NS; + +// Aliases can't be used when declaring names. +// CHECK:STDERR: fail_decl_in_alias.carbon:[[@LINE+6]]:7: ERROR: Name qualifiers are only allowed for entities that provide a scope. +// CHECK:STDERR: fn ns.A() -> i32 { return 0; } +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_decl_in_alias.carbon:[[@LINE+3]]:4: Non-scope entity referenced here. +// CHECK:STDERR: fn ns.A() -> i32 { return 0; } +// CHECK:STDERR: ^~ +fn ns.A() -> i32 { return 0; } + +// CHECK:STDOUT: --- fail_decl_in_alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: i32 = int_literal 0 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.NS = %.loc7, .ns = %ns} [template] +// CHECK:STDOUT: %.loc7: = namespace {} [template] +// CHECK:STDOUT: %NS.ref: = name_ref NS, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %ns: = bind_alias ns, %.loc7 [template = %.loc7] +// CHECK:STDOUT: %.loc18: = fn_decl @.1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.1() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc18: i32 = int_literal 0 [template = constants.%.1] +// CHECK:STDOUT: return %.loc18 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 05ce76425e1e..fc9dac931656 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -118,6 +118,9 @@ CARBON_DIAGNOSTIC_KIND(ExpectedAfterBase) CARBON_DIAGNOSTIC_KIND(ImplExpectedAfterForall) CARBON_DIAGNOSTIC_KIND(ImplExpectedAs) +// Alias diagnostics. +CARBON_DIAGNOSTIC_KIND(ExpectedAliasInitializer) + // ============================================================================ // Semantics diagnostics // ============================================================================ @@ -250,6 +253,9 @@ CARBON_DIAGNOSTIC_KIND(ModifierNotAllowedWith) CARBON_DIAGNOSTIC_KIND(ModifierMustAppearBefore) CARBON_DIAGNOSTIC_KIND(ModifierPrevious) +// Alias diagnostics. +CARBON_DIAGNOSTIC_KIND(AliasRequiresNameRef) + // ============================================================================ // Other diagnostics // ============================================================================ diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index e37d357391c3..5ab88a712bbd 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -60,6 +60,16 @@ auto HandleAssign(FunctionContext& context, SemIR::InstId /*inst_id*/, context.FinishInit(storage_type_id, inst.lhs_id, inst.rhs_id); } +auto HandleBindAlias(FunctionContext& context, SemIR::InstId inst_id, + SemIR::BindAlias inst) -> void { + auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id); + if (type_inst_id == SemIR::InstId::BuiltinNamespaceType) { + return; + } + + context.SetLocal(inst_id, context.GetValue(inst.value_id)); +} + auto HandleBindName(FunctionContext& context, SemIR::InstId inst_id, SemIR::BindName inst) -> void { context.SetLocal(inst_id, context.GetValue(inst.value_id)); diff --git a/toolchain/lower/testdata/alias/local.carbon b/toolchain/lower/testdata/alias/local.carbon new file mode 100644 index 000000000000..a00d773a53bc --- /dev/null +++ b/toolchain/lower/testdata/alias/local.carbon @@ -0,0 +1,21 @@ +// 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 + +fn F() -> i32 { + var a: i32 = 0; + alias b = a; + return b; +} + +// CHECK:STDOUT: ; ModuleID = 'local.carbon' +// CHECK:STDOUT: source_filename = "local.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @F() { +// CHECK:STDOUT: %a = alloca i32, align 4 +// CHECK:STDOUT: store i32 0, ptr %a, align 4 +// CHECK:STDOUT: %1 = load i32, ptr %a, align 4 +// CHECK:STDOUT: ret i32 %1 +// CHECK:STDOUT: } diff --git a/toolchain/parse/handle_alias.cpp b/toolchain/parse/handle_alias.cpp new file mode 100644 index 000000000000..74fbf7e7a5d4 --- /dev/null +++ b/toolchain/parse/handle_alias.cpp @@ -0,0 +1,61 @@ +// 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/lex/token_kind.h" +#include "toolchain/lex/tokenized_buffer.h" +#include "toolchain/parse/context.h" +#include "toolchain/parse/node_kind.h" +#include "toolchain/parse/state.h" + +namespace Carbon::Parse { + +auto HandleAlias(Context& context) -> void { + auto state = context.PopState(); + + context.PushState(state, State::AliasAfterName); + context.PushState(State::DeclNameAndParamsAsNone, state.token); +} + +auto HandleAliasAfterName(Context& context) -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.RecoverFromDeclError(state, NodeKind::Alias, + /*skip_past_likely_end=*/true); + return; + } + + if (auto equal = context.ConsumeIf(Lex::TokenKind::Equal)) { + context.AddLeafNode(NodeKind::AliasInitializer, *equal); + context.PushState(state, State::AliasFinish); + context.PushState(State::Expr); + } else { + CARBON_DIAGNOSTIC(ExpectedAliasInitializer, Error, + "`alias` requires a `=` for the source."); + context.emitter().Emit(*context.position(), ExpectedAliasInitializer); + context.RecoverFromDeclError(state, NodeKind::Alias, + /*skip_past_likely_end=*/true); + } +} + +auto HandleAliasFinish(Context& context) -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.RecoverFromDeclError(state, NodeKind::Alias, + /*skip_past_likely_end=*/true); + return; + } + + if (auto semi = context.ConsumeIf(Lex::TokenKind::Semi)) { + context.AddNode(NodeKind::Alias, *semi, state.subtree_start, + state.has_error); + } else { + context.EmitExpectedDeclSemi(Lex::TokenKind::Alias); + context.RecoverFromDeclError(state, NodeKind::Alias, + /*skip_past_likely_end=*/true); + } +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index 7827a7c38191..bd51952c8ec6 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -110,6 +110,10 @@ static auto HandleBaseAsDecl(Context& context, Context::StateStackEntry state) static auto TryHandleAsDecl(Context& context, Context::StateStackEntry state, bool saw_modifier) -> bool { switch (context.PositionKind()) { + case Lex::TokenKind::Alias: { + ApplyIntroducer(context, state, NodeKind::AliasIntroducer, State::Alias); + return true; + } case Lex::TokenKind::Base: { HandleBaseAsDecl(context, state); return true; @@ -195,6 +199,7 @@ static auto ResolveAmbiguousTokenAsDeclaration(Context& context, // also modifiers (such as `base`). Other introducer tokens need to be // added by hand. switch (context.PositionKind(Lookahead::NextToken)) { + case Lex::TokenKind::Alias: case Lex::TokenKind::Class: case Lex::TokenKind::Constraint: case Lex::TokenKind::Fn: diff --git a/toolchain/parse/handle_statement.cpp b/toolchain/parse/handle_statement.cpp index 5b1e53f1eced..0d0f60693152 100644 --- a/toolchain/parse/handle_statement.cpp +++ b/toolchain/parse/handle_statement.cpp @@ -10,6 +10,11 @@ auto HandleStatement(Context& context) -> void { context.PopAndDiscardState(); switch (context.PositionKind()) { + case Lex::TokenKind::Alias: { + context.PushState(State::Alias); + context.AddLeafNode(NodeKind::AliasIntroducer, context.Consume()); + break; + } case Lex::TokenKind::Break: { context.PushState(State::StatementBreakFinish); context.AddLeafNode(NodeKind::BreakStatementStart, context.Consume()); diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 7080b551d3ae..7b37d76893b1 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -256,6 +256,17 @@ CARBON_PARSE_NODE_KIND_BRACKET(FunctionDefinition, FunctionDefinitionStart, CARBON_PARSE_NODE_KIND_BRACKET(FunctionDecl, FunctionIntroducer, CARBON_IF_VALID(Semi)) +// `alias`: +// AliasIntroducer +// _repeated_ _external_: modifier +// _external_: IdentifierName or QualifiedName +// AliasInitializer +// _external_: expression +// Alias +CARBON_PARSE_NODE_KIND_CHILD_COUNT(AliasIntroducer, 0, Alias) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(AliasInitializer, 0, Equal) +CARBON_PARSE_NODE_KIND_BRACKET(Alias, AliasIntroducer, CARBON_IF_VALID(Semi)) + // A tuple pattern: // TuplePatternStart // _external_: [Generic]BindingPattern diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index f42f79008a9a..ca43f8857698 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -307,6 +307,11 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit) // ^~~~~~~ // 1. DeclScopeLoop // +// alias ... +// ^~~~~ +// 1. Alias +// 2. DeclScopeLoop +// // class ... // ^~~~~ // 1. TypeAfterIntroducerAsClass @@ -672,6 +677,35 @@ CARBON_PARSE_STATE(NamespaceFinish) // (state done) CARBON_PARSE_STATE(Package) +// Handles `alias` after the introducer. +// +// alias ... +// ^ +// 1. DeclNameAndParamsAsNone +// 2. AliasInitializer +CARBON_PARSE_STATE(Alias) + +// Handles `alias` after the name. +// +// alias ... = ... ; +// ^ +// 1. Expr +// 2. AliasFinish +// +// alias ??? +// ^ +// (state done) +CARBON_PARSE_STATE(AliasAfterName) + +// Handles `alias` at the end. +// +// alias ... ; +// ^ +// alias ??? +// ^ +// (state done) +CARBON_PARSE_STATE(AliasFinish) + // Starts parsing a pattern in a comma-separated list. The variants mark // whether it is part of an implicit parameter list or a tuple pattern. // diff --git a/toolchain/parse/testdata/alias/basic.carbon b/toolchain/parse/testdata/alias/basic.carbon new file mode 100644 index 000000000000..ec2c72493087 --- /dev/null +++ b/toolchain/parse/testdata/alias/basic.carbon @@ -0,0 +1,69 @@ +// 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 + +alias a = foo; +alias b = 0; +alias c = 1 + 3; + +alias NS.ns = foo; + +class C { + alias c = foo; +} + +fn F() { + alias f = foo; +} + +// CHECK:STDOUT: - filename: basic.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'foo'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'b'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '0'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'c'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '1'}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '3'}, +// CHECK:STDOUT: {kind: 'InfixOperatorPlus', text: '+', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'NS'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'ns'}, +// CHECK:STDOUT: {kind: 'QualifiedName', text: '.', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'foo'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'c'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'foo'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'f'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'foo'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/alias/fail_syntax.carbon b/toolchain/parse/testdata/alias/fail_syntax.carbon new file mode 100644 index 000000000000..3ed9d9c13695 --- /dev/null +++ b/toolchain/parse/testdata/alias/fail_syntax.carbon @@ -0,0 +1,117 @@ +// 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 + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:7: ERROR: `alias` introducer should be followed by a name. +// CHECK:STDERR: alias = b; +// CHECK:STDERR: ^ +alias = b; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:9: ERROR: `alias` requires a `=` for the source. +// CHECK:STDERR: alias a b; +// CHECK:STDERR: ^ +alias a b; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:10: ERROR: Expected expression. +// CHECK:STDERR: alias a =; +// CHECK:STDERR: ^ +alias a =; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:8: ERROR: `alias` requires a `=` for the source. +// CHECK:STDERR: alias a; +// CHECK:STDERR: ^ +alias a; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:7: ERROR: `alias` introducer should be followed by a name. +// CHECK:STDERR: alias =; +// CHECK:STDERR: ^ +alias =; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:9: ERROR: `alias` requires a `=` for the source. +// CHECK:STDERR: alias a b c = d; +// CHECK:STDERR: ^ +alias a b c = d; + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:13: ERROR: `alias` declarations must end with a `;`. +// CHECK:STDERR: alias a = b c d; +// CHECK:STDERR: ^ +alias a = b c d; + +fn F() { + // CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:8: ERROR: `alias` introducer should be followed by a name. + // CHECK:STDERR: alias; + // CHECK:STDERR: ^ + alias; + + // CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:12: ERROR: Expected expression. + // CHECK:STDERR: alias a =; + // CHECK:STDERR: ^ + alias a =; + + alias a = +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+3]]:1: ERROR: Expected expression. +// CHECK:STDERR: } +// CHECK:STDERR: ^ +} + +alias a = b + +// CHECK:STDERR: fail_syntax.carbon:[[@LINE+56]]:21: ERROR: `alias` declarations must end with a `;`. +// CHECK:STDERR: // CHECK:STDOUT: ] +// CHECK:STDERR: ^ +// CHECK:STDOUT: - filename: fail_syntax.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '=', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '=', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'Alias', text: '=', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'AliasIntroducer', text: 'alias'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'AliasInitializer', text: '='}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'b'}, +// CHECK:STDOUT: {kind: 'Alias', text: 'b', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index d5313dc462f6..02ab13a7ec2b 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -312,6 +312,25 @@ struct FunctionDefinition { llvm::SmallVector body; }; +// `alias` nodes +// ------------- + +using AliasIntroducer = LeafNode; +using AliasInitializer = LeafNode; + +// An `alias` declaration: `alias a = b;`. +struct Alias { + static constexpr auto Kind = + NodeKind::Alias.Define(NodeCategory::Decl | NodeCategory::Statement); + + AliasIntroducerId introducer; + llvm::SmallVector modifiers; + // For now, this is either an IdentifierName or a QualifiedName. + AnyNameComponentId name; + AliasInitializerId equals; + AnyExprId initializer; +}; + // `let` nodes // ----------- diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index b8be1a3cf196..026ea53c9094 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -175,7 +175,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { for (int i : llvm::seq(start, insts_.size())) { auto id = InstId(i); auto value = constant_values_.Get(id); - if (value.is_constant()) { + if (!value.is_valid() || value.is_constant()) { map.Add(PrintToString(id), Yaml::OutputScalar(value)); } } @@ -191,6 +191,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { static auto GetTypePrecedence(InstKind kind) -> int { switch (kind) { case ArrayType::Kind: + case BindAlias::Kind: case BindSymbolicName::Kind: case Builtin::Kind: case ClassType::Kind: @@ -315,8 +316,9 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, } break; } + case BindAlias::Kind: case BindSymbolicName::Kind: { - auto name_id = inst.As().bind_name_id; + auto name_id = inst.As().bind_name_id; out << sem_ir.names().GetFormatted( sem_ir.bind_names().Get(name_id).name_id); break; @@ -535,6 +537,10 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { continue; } + case BindAlias::Kind: { + inst_id = inst.As().value_id; + continue; + } case NameRef::Kind: { inst_id = inst.As().value_id; continue; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 5466c141cc02..62b67e9f1fd1 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -465,6 +465,7 @@ class InstNamer { CollectNamesInBlock(scope_id, inst.As().block_id); break; } + case BindAlias::Kind: case BindName::Kind: case BindSymbolicName::Kind: { add_inst_name_id(sem_ir_.bind_names() diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index b1df6231af2e..92abe2abb93d 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -24,8 +24,9 @@ CARBON_SEM_IR_INST_KIND(ArrayInit) CARBON_SEM_IR_INST_KIND(ArrayType) CARBON_SEM_IR_INST_KIND(Assign) CARBON_SEM_IR_INST_KIND(BaseDecl) -CARBON_SEM_IR_INST_KIND(BindSymbolicName) +CARBON_SEM_IR_INST_KIND(BindAlias) CARBON_SEM_IR_INST_KIND(BindName) +CARBON_SEM_IR_INST_KIND(BindSymbolicName) CARBON_SEM_IR_INST_KIND(BindValue) CARBON_SEM_IR_INST_KIND(BlockArg) CARBON_SEM_IR_INST_KIND(BoolLiteral) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index e8e8ea527732..cba36bd84574 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -169,7 +169,7 @@ struct BaseDecl { // Common representation for both kinds of `bind*name` node. struct AnyBindName { // TODO: Also handle BindTemplateName once it exists. - static constexpr InstKind Kinds[] = {InstKind::BindName, + static constexpr InstKind Kinds[] = {InstKind::BindAlias, InstKind::BindName, InstKind::BindSymbolicName}; InstKind kind; @@ -178,9 +178,9 @@ struct AnyBindName { InstId value_id; }; -struct BindSymbolicName { +struct BindAlias { static constexpr auto Kind = - InstKind::BindSymbolicName.Define("bind_symbolic_name"); + InstKind::BindAlias.Define("bind_alias"); TypeId type_id; BindNameId bind_name_id; @@ -199,6 +199,15 @@ struct BindName { InstId value_id; }; +struct BindSymbolicName { + static constexpr auto Kind = + InstKind::BindSymbolicName.Define("bind_symbolic_name"); + + TypeId type_id; + BindNameId bind_name_id; + InstId value_id; +}; + struct BindValue { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind =