From a6508fcf05f5e78c0e9c6b6fefac8eaf2c34dd96 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 4 Jan 2024 19:39:45 -0800 Subject: [PATCH] Basic support for generic bindings. (#3555) This change adds a `BindSymbolicName` instruction for generic bindings, paralleling the existing `BindName`. A mechanism is also added to allow both kinds of binding to be accessed uniformly, for convenience in the case where the two different kinds of binding are treated the same. Generic bindings of type `type` are allowed to be used as types, although no operations are provided for such types. For now lowering treats these types as empty structs, which seems like a reasonable lowering for non-monomorphized unconstrained types. --- toolchain/check/context.cpp | 7 +++ toolchain/check/convert.cpp | 4 ++ toolchain/check/handle_binding_pattern.cpp | 57 ++++++++++++------- toolchain/check/handle_class.cpp | 4 ++ toolchain/check/handle_function.cpp | 2 +- toolchain/check/handle_interface.cpp | 4 ++ toolchain/check/handle_let.cpp | 5 +- toolchain/check/handle_variable.cpp | 6 +- toolchain/check/node_stack.h | 13 +++++ .../generic/fail_type_param_mismatch.carbon | 35 ++++++++++++ .../function/generic/type_param.carbon | 33 +++++++++++ .../check/testdata/let/fail_generic.carbon | 36 ++++++++++++ toolchain/check/testdata/let/generic.carbon | 37 ++++++++++++ .../check/testdata/var/fail_generic.carbon | 29 ++++++++++ toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/lower/file_context.cpp | 7 ++- toolchain/lower/handle.cpp | 5 ++ .../function/generic/type_param.carbon | 22 +++++++ toolchain/parse/node_kind.def | 2 + toolchain/sem_ir/file.cpp | 9 ++- toolchain/sem_ir/formatter.cpp | 5 +- toolchain/sem_ir/inst.h | 5 +- toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 25 +++++++- 24 files changed, 321 insertions(+), 33 deletions(-) create mode 100644 toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon create mode 100644 toolchain/check/testdata/function/generic/type_param.carbon create mode 100644 toolchain/check/testdata/let/fail_generic.carbon create mode 100644 toolchain/check/testdata/let/generic.carbon create mode 100644 toolchain/check/testdata/var/fail_generic.carbon create mode 100644 toolchain/lower/testdata/function/generic/type_param.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 8d19738d276e..d990dd4a079d 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -1009,6 +1009,7 @@ class TypeCompleter { case SemIR::Builtin::Kind: CARBON_FATAL() << "Builtins should be named as cross-references"; + case SemIR::BindSymbolicName::Kind: case SemIR::PointerType::Kind: case SemIR::UnboundElementType::Kind: return MakeCopyValueRepr(type_id); @@ -1126,6 +1127,12 @@ static auto ProfileType(Context& semantics_context, SemIR::Inst inst, .GetUnqualifiedType(inst.As().inner_id) .index); break; + case SemIR::BindSymbolicName::Kind: + // TODO: Use de Bruijn levels or similar to identify equivalent type + // bindings across redeclarations. + canonical_id.AddInteger( + inst.As().bind_name_id.index); + break; case SemIR::PointerType::Kind: canonical_id.AddInteger(inst.As().pointee_id.index); break; diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 5f3b99e0edb1..42db7800cd28 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -51,6 +51,10 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir, if (!SemIR::GetInitRepr(sem_ir, call.type_id).has_return_slot()) { return SemIR::InstId::Invalid; } + if (!call.args_id.is_valid()) { + // Argument initialization failed, so we have no return slot. + return SemIR::InstId::Invalid; + } return sem_ir.inst_blocks().Get(call.args_id).back(); } } diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 888b9611dfe0..df78d1257c62 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -10,10 +10,10 @@ namespace Carbon::Check { auto HandleAddress(Context& context, Parse::AddressId parse_node) -> bool { - auto self_param_id = - context.node_stack().Pop(); - auto self_param = context.insts().TryGetAs(self_param_id); - if (self_param && + auto self_param_id = context.node_stack().PopPattern(); + if (auto self_param = + context.insts().TryGetAs(self_param_id); + self_param && context.bind_names().Get(self_param->bind_name_id).name_id == SemIR::NameId::SelfValue) { // TODO: The type of an `addr_pattern` should probably be the non-pointer @@ -30,14 +30,8 @@ auto HandleAddress(Context& context, Parse::AddressId parse_node) -> bool { return true; } -auto HandleGenericBindingPattern(Context& context, - Parse::GenericBindingPatternId parse_node) - -> bool { - return context.TODO(parse_node, "GenericBindingPattern"); -} - -auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) - -> bool { +auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node, + bool is_generic) -> bool { auto [type_node, parsed_type_id] = context.node_stack().PopExprWithParseNode(); auto type_node_copy = type_node; @@ -49,8 +43,6 @@ auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) auto [name_node, name_id] = context.node_stack().PopNameWithParseNode(); // Create the appropriate kind of binding for this pattern. - // - // TODO: Update this to create a generic or template binding as needed. auto make_bind_name = [&, name_node = name_node, name_id = name_id]( SemIR::TypeId type_id, SemIR::InstId value_id) -> SemIR::Inst { @@ -58,7 +50,13 @@ auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) auto bind_name_id = context.bind_names().Add( {.name_id = name_id, .enclosing_scope_id = SemIR::NameScopeId::Invalid}); - return SemIR::BindName{name_node, type_id, bind_name_id, value_id}; + if (is_generic) { + // TODO: Create a `BindTemplateName` instead inside a `template` pattern. + return SemIR::BindSymbolicName{name_node, type_id, bind_name_id, + value_id}; + } else { + return SemIR::BindName{name_node, type_id, bind_name_id, value_id}; + } }; // A `self` binding can only appear in an implicit parameter list. @@ -78,6 +76,17 @@ auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) context.node_stack().PeekParseNodeKind()) { case Parse::NodeKind::ReturnedModifier: case Parse::NodeKind::VariableIntroducer: { + if (is_generic) { + CARBON_DIAGNOSTIC( + CompileTimeBindingInVarDecl, Error, + "`var` declaration cannot declare a compile-time binding."); + context.emitter().Emit(type_node, CompileTimeBindingInVarDecl); + } + auto binding_id = + is_generic + ? Parse::NodeId::Invalid + : context.parse_tree().As(parse_node); + // A `var` declaration at class scope introduces a field. auto enclosing_class_decl = context.GetCurrentScopeAs(); cast_type_id = context.AsCompleteType(cast_type_id, [&] { @@ -103,18 +112,18 @@ auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) auto& class_info = context.classes().Get(enclosing_class_decl->class_id); auto field_type_inst_id = context.AddInst(SemIR::UnboundElementType{ - parse_node, context.GetBuiltinType(SemIR::BuiltinKind::TypeType), + binding_id, context.GetBuiltinType(SemIR::BuiltinKind::TypeType), class_info.self_type_id, cast_type_id}); value_type_id = context.CanonicalizeType(field_type_inst_id); value_id = context.AddInst( - SemIR::FieldDecl{parse_node, value_type_id, name_id, + SemIR::FieldDecl{binding_id, value_type_id, name_id, SemIR::ElementIndex(context.args_type_info_stack() .PeekCurrentBlockContents() .size())}); // Add a corresponding field to the object representation of the class. context.args_type_info_stack().AddInst( - SemIR::StructTypeField{parse_node, name_id, cast_type_id}); + SemIR::StructTypeField{binding_id, name_id, cast_type_id}); } else { value_id = context.AddInst( SemIR::VarStorage{name_node, value_type_id, name_id}); @@ -166,8 +175,18 @@ auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) return true; } +auto HandleBindingPattern(Context& context, Parse::BindingPatternId parse_node) + -> bool { + return HandleAnyBindingPattern(context, parse_node, /*is_generic=*/false); +} + +auto HandleGenericBindingPattern(Context& context, + Parse::GenericBindingPatternId parse_node) + -> bool { + return HandleAnyBindingPattern(context, parse_node, /*is_generic=*/true); +} + auto HandleTemplate(Context& context, Parse::TemplateId parse_node) -> bool { - // TODO: diagnose if this occurs in a `var` context. return context.TODO(parse_node, "HandleTemplate"); } diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 4e0ecfe05005..367361607051 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -23,6 +23,10 @@ auto HandleClassIntroducer(Context& context, static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId parse_node) -> std::tuple { + if (context.node_stack().PopIf()) { + context.TODO(parse_node, "generic class"); + } + auto name_context = context.decl_name_stack().FinishName(); context.node_stack() .PopAndDiscardSoloParseNode(); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 6eeb279dc29f..68da8eeb9bd5 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -261,7 +261,7 @@ auto HandleFunctionDefinitionStart(Context& context, context.sem_ir().StringifyType(param.type_id())); }); - if (auto fn_param = param.TryAs()) { + if (auto fn_param = param.TryAs()) { context.AddNameToLookup( fn_param->parse_node, context.bind_names().Get(fn_param->bind_name_id).name_id, param_id); diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 3f4a311973c4..4af9dd49b6d0 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -24,6 +24,10 @@ auto HandleInterfaceIntroducer(Context& context, static auto BuildInterfaceDecl(Context& context, Parse::AnyInterfaceDeclId parse_node) -> std::tuple { + if (context.node_stack().PopIf()) { + context.TODO(parse_node, "generic interface"); + } + auto name_context = context.decl_name_stack().FinishName(); context.node_stack() .PopAndDiscardSoloParseNode(); diff --git a/toolchain/check/handle_let.cpp b/toolchain/check/handle_let.cpp index a304225f4b51..09839bd01a19 100644 --- a/toolchain/check/handle_let.cpp +++ b/toolchain/check/handle_let.cpp @@ -14,8 +14,7 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool { if (context.node_stack().PeekIs()) { return context.TODO(parse_node, "tuple pattern in let"); } - SemIR::InstId pattern_id = - context.node_stack().Pop(); + SemIR::InstId pattern_id = context.node_stack().PopPattern(); context.node_stack() .PopAndDiscardSoloParseNode(); // Process declaration modifiers. @@ -44,7 +43,7 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool { // Update the binding with its value and add it to the current block, after // the computation of the value. // TODO: Support other kinds of pattern here. - auto bind_name = pattern.As(); + auto bind_name = pattern.As(); CARBON_CHECK(!bind_name.value_id.is_valid()) << "Binding should not already have a value!"; bind_name.value_id = value_id; diff --git a/toolchain/check/handle_variable.cpp b/toolchain/check/handle_variable.cpp index 0783d0bed2ca..76c680edefd9 100644 --- a/toolchain/check/handle_variable.cpp +++ b/toolchain/check/handle_variable.cpp @@ -53,8 +53,9 @@ auto HandleVariableDecl(Context& context, Parse::VariableDeclId parse_node) } // Extract the name binding. - auto value_id = context.node_stack().Pop(); - if (auto bind_name = context.insts().Get(value_id).TryAs()) { + auto value_id = context.node_stack().PopPattern(); + if (auto bind_name = + context.insts().Get(value_id).TryAs()) { // Form a corresponding name in the current context, and bind the name to // the variable. auto name_context = context.decl_name_stack().MakeUnqualifiedName( @@ -63,6 +64,7 @@ auto HandleVariableDecl(Context& context, Parse::VariableDeclId parse_node) context.decl_name_stack().AddNameToLookup(name_context, value_id); value_id = bind_name->value_id; } + // TODO: Handle other kinds of pattern. // Pop the `returned` specifier if present. context.node_stack() diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 0cc0e4c7cc0e..179217782a97 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -134,6 +134,12 @@ class NodeStack { return PopWithParseNode(); } + // Pops a pattern from the top of the stack and returns the parse_node and + // the ID. + auto PopPatternWithParseNode() -> std::pair { + return PopWithParseNode(); + } + // Pops a name from the top of the stack and returns the parse_node and // the ID. auto PopNameWithParseNode() -> std::pair { @@ -203,6 +209,12 @@ class NodeStack { // Expressions map multiple Parse::NodeKinds to SemIR::InstId always. auto PopExpr() -> SemIR::InstId { return PopExprWithParseNode().second; } + // Pops a pattern from the top of the stack and returns the ID. + // Patterns map multiple Parse::NodeKinds to SemIR::InstId always. + auto PopPattern() -> SemIR::InstId { + return PopPatternWithParseNode().second; + } + // Pops a name from the top of the stack and returns the ID. auto PopName() -> SemIR::NameId { return PopNameWithParseNode().second; } @@ -359,6 +371,7 @@ class NodeStack { case Parse::NodeKind::BindingPattern: case Parse::NodeKind::CallExpr: case Parse::NodeKind::CallExprStart: + case Parse::NodeKind::GenericBindingPattern: case Parse::NodeKind::IdentifierNameExpr: case Parse::NodeKind::IfExprThen: case Parse::NodeKind::IfExprElse: diff --git a/toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon b/toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon new file mode 100644 index 000000000000..6822380b0480 --- /dev/null +++ b/toolchain/check/testdata/function/generic/fail_type_param_mismatch.carbon @@ -0,0 +1,35 @@ +// 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(T:! type, U:! type) { + var p: T*; + // CHECK:STDERR: fail_type_param_mismatch.carbon:[[@LINE+3]]:3: ERROR: Cannot implicitly convert from `T` to `U`. + // CHECK:STDERR: let n: U = *p; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + let n: U = *p; +} + +// CHECK:STDOUT: --- fail_type_param_mismatch.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%T: type, %U: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %T.ref: type = name_ref T, %T +// CHECK:STDOUT: %.loc8: type = ptr_type T +// CHECK:STDOUT: %p.var: ref T* = var p +// CHECK:STDOUT: %p: ref T* = bind_name p, %p.var +// CHECK:STDOUT: %U.ref: type = name_ref U, %U +// CHECK:STDOUT: %p.ref: ref T* = name_ref p, %p +// CHECK:STDOUT: %.loc12_15: T* = bind_value %p.ref +// CHECK:STDOUT: %.loc12_14: ref T = deref %.loc12_15 +// CHECK:STDOUT: %n: U = bind_name n, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/generic/type_param.carbon b/toolchain/check/testdata/function/generic/type_param.carbon new file mode 100644 index 000000000000..29238e3b9a2f --- /dev/null +++ b/toolchain/check/testdata/function/generic/type_param.carbon @@ -0,0 +1,33 @@ +// 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(T:! type) { + var p: T*; + let n: T = *p; +} + +// CHECK:STDOUT: --- type_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%T: type) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %T.ref.loc8: type = name_ref T, %T +// CHECK:STDOUT: %.loc8: type = ptr_type T +// CHECK:STDOUT: %p.var: ref T* = var p +// CHECK:STDOUT: %p: ref T* = bind_name p, %p.var +// CHECK:STDOUT: %T.ref.loc9: type = name_ref T, %T +// CHECK:STDOUT: %p.ref: ref T* = name_ref p, %p +// CHECK:STDOUT: %.loc9_15: T* = bind_value %p.ref +// CHECK:STDOUT: %.loc9_14.1: ref T = deref %.loc9_15 +// CHECK:STDOUT: %.loc9_14.2: T = bind_value %.loc9_14.1 +// CHECK:STDOUT: %n: T = bind_name n, %.loc9_14.2 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/let/fail_generic.carbon b/toolchain/check/testdata/let/fail_generic.carbon new file mode 100644 index 000000000000..8677107202f4 --- /dev/null +++ b/toolchain/check/testdata/let/fail_generic.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 + +// TODO: Should this be valid? +fn F(a: i32) -> i32 { + let T:! type = i32; + // CHECK:STDERR: fail_generic.carbon:[[@LINE+3]]:3: ERROR: Cannot implicitly convert from `i32` to `T`. + // CHECK:STDERR: let x: T = 5; + // CHECK:STDERR: ^~~~~~~~~~~~~ + let x: T = 5; + // CHECK:STDERR: fail_generic.carbon:[[@LINE+3]]:3: ERROR: Cannot implicitly convert from `T` to `i32`. + // CHECK:STDERR: return x; + // CHECK:STDERR: ^~~~~~~~~ + return x; +} + +// CHECK:STDOUT: --- fail_generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%a: i32) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %T: type = bind_symbolic_name T, i32 +// CHECK:STDOUT: %T.ref: type = name_ref T, %T +// CHECK:STDOUT: %.loc13: i32 = int_literal 5 +// CHECK:STDOUT: %x: T = bind_name x, +// CHECK:STDOUT: %x.ref: T = name_ref x, %x +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/let/generic.carbon b/toolchain/check/testdata/let/generic.carbon new file mode 100644 index 000000000000..d8cbe00aeaaa --- /dev/null +++ b/toolchain/check/testdata/let/generic.carbon @@ -0,0 +1,37 @@ +// 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() { + let T:! type = i32; + var p: T*; + var a: T = *p; +} + +// CHECK:STDOUT: --- generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.F = %F} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %T: type = bind_symbolic_name T, i32 +// CHECK:STDOUT: %T.ref.loc9: type = name_ref T, %T +// CHECK:STDOUT: %.loc9: type = ptr_type T +// CHECK:STDOUT: %p.var: ref T* = var p +// CHECK:STDOUT: %p: ref T* = bind_name p, %p.var +// CHECK:STDOUT: %T.ref.loc10: type = name_ref T, %T +// CHECK:STDOUT: %a.var: ref T = var a +// CHECK:STDOUT: %a: ref T = bind_name a, %a.var +// CHECK:STDOUT: %p.ref: ref T* = name_ref p, %p +// CHECK:STDOUT: %.loc10_15: T* = bind_value %p.ref +// CHECK:STDOUT: %.loc10_14.1: ref T = deref %.loc10_15 +// CHECK:STDOUT: %.loc10_14.2: T = bind_value %.loc10_14.1 +// CHECK:STDOUT: assign %a.var, %.loc10_14.2 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/var/fail_generic.carbon b/toolchain/check/testdata/var/fail_generic.carbon new file mode 100644 index 000000000000..4b7cbdfc30d4 --- /dev/null +++ b/toolchain/check/testdata/var/fail_generic.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 + +fn Main() { + // CHECK:STDERR: fail_generic.carbon:[[@LINE+3]]:11: ERROR: `var` declaration cannot declare a compile-time binding. + // CHECK:STDERR: var x:! i32 = 0; + // CHECK:STDERR: ^~~ + var x:! i32 = 0; +} + +// CHECK:STDOUT: --- fail_generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace {.Main = %Main} +// CHECK:STDOUT: %Main: = fn_decl @Main +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Main() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.var: ref i32 = var x +// CHECK:STDOUT: %x: i32 = bind_symbolic_name x, %x.var +// CHECK:STDOUT: %.loc11: i32 = int_literal 0 +// CHECK:STDOUT: assign %x.var, %.loc11 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 7a0c03088c6f..90adca974078 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -174,6 +174,7 @@ CARBON_DIAGNOSTIC_KIND(DerefOfNonPointer) CARBON_DIAGNOSTIC_KIND(DerefOfType) CARBON_DIAGNOSTIC_KIND(FunctionPreviousDefinition) CARBON_DIAGNOSTIC_KIND(FunctionRedefinition) +CARBON_DIAGNOSTIC_KIND(CompileTimeBindingInVarDecl) CARBON_DIAGNOSTIC_KIND(NameAmbiguousDueToExtend) CARBON_DIAGNOSTIC_KIND(NameNotFound) CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index a0f2f8eabe06..96b97639eee9 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -219,7 +219,9 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) sem_ir().insts().TryGetAs(param_ref_id)) { bind_name_id = addr->inner_id; } - CARBON_CHECK(sem_ir().insts().TryGetAs(bind_name_id)); + auto bind_name = sem_ir().insts().Get(bind_name_id); + // TODO: Should we stop passing compile-time bindings at runtime? + CARBON_CHECK(bind_name.Is()); function_lowering.SetLocal(bind_name_id, param_value); } @@ -272,6 +274,9 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { GetType(array_type.element_type_id), sem_ir_->GetArrayBoundValue(array_type.bound_id)); } + case SemIR::BindSymbolicName::Kind: + // Treat non-monomorphized type bindings as opaque. + return llvm::StructType::get(*llvm_context_); case SemIR::ClassType::Kind: { auto object_repr_id = sem_ir_->classes() .Get(inst.As().class_id) diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 9b8cd0e0e500..e8e0cde47252 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -65,6 +65,11 @@ auto HandleBindName(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetValue(inst.value_id)); } +auto HandleBindSymbolicName(FunctionContext& context, SemIR::InstId inst_id, + SemIR::BindSymbolicName inst) -> void { + context.SetLocal(inst_id, context.GetValue(inst.value_id)); +} + auto HandleBlockArg(FunctionContext& context, SemIR::InstId inst_id, SemIR::BlockArg inst) -> void { context.SetLocal(inst_id, context.GetBlockArg(inst.block_id, inst.type_id)); diff --git a/toolchain/lower/testdata/function/generic/type_param.carbon b/toolchain/lower/testdata/function/generic/type_param.carbon new file mode 100644 index 000000000000..3d523fb6acad --- /dev/null +++ b/toolchain/lower/testdata/function/generic/type_param.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 + +fn F(T:! type) { + var p: T*; + let n: T = *p; +} + +// CHECK:STDOUT: ; ModuleID = 'type_param.carbon' +// CHECK:STDOUT: source_filename = "type_param.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: %type = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: define void @F(%type %T) { +// CHECK:STDOUT: %p = alloca ptr, align 8 +// CHECK:STDOUT: %1 = load ptr, ptr %p, align 8 +// CHECK:STDOUT: %2 = load {}, ptr %1, align 1 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 45c7cfbf570d..1069967010ea 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -298,6 +298,8 @@ CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpr, ArrayExprSemi, CloseSquareBracket) // [Generic]BindingPattern // _optional_ Address // _optional_ Template +// +// TODO: Rename GenericBindingPattern to CompileTimeBindingPattern. CARBON_PARSE_NODE_KIND_CHILD_COUNT(BindingPattern, 2, CARBON_IF_VALID(Colon)) CARBON_PARSE_NODE_KIND_CHILD_COUNT(GenericBindingPattern, 2, ColonExclaim) CARBON_PARSE_NODE_KIND_CHILD_COUNT(Address, 1, Addr) diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 531ff6957f91..f08ea22d9e6d 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -26,7 +26,7 @@ auto Function::GetParamFromParamRefId(const File& sem_ir, InstId param_ref_id) ref = sem_ir.insts().Get(param_ref_id); } - if (auto bind_name = ref.TryAs()) { + if (auto bind_name = ref.TryAs()) { param_ref_id = bind_name->value_id; ref = sem_ir.insts().Get(param_ref_id); } @@ -178,6 +178,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (kind) { case ArrayType::Kind: + case BindSymbolicName::Kind: case Builtin::Kind: case ClassType::Kind: case NameRef::Kind: @@ -300,6 +301,11 @@ auto File::StringifyTypeExpr(InstId outer_inst_id) const -> std::string { } break; } + case BindSymbolicName::Kind: { + auto name_id = inst.As().bind_name_id; + out << names().GetFormatted(bind_names().Get(name_id).name_id); + break; + } case ClassType::Kind: { auto class_name_id = classes().Get(inst.As().class_id).name_id; @@ -509,6 +515,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case AddressOf::Kind: case AddrPattern::Kind: case ArrayType::Kind: + case BindSymbolicName::Kind: case BindValue::Kind: case BlockArg::Kind: case BoolLiteral::Kind: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 9b5d2808c655..871ce27ffc74 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -462,9 +462,10 @@ class InstNamer { CollectNamesInBlock(scope_idx, inst.As().block_id); break; } - case BindName::Kind: { + case BindName::Kind: + case BindSymbolicName::Kind: { add_inst_name_id(sem_ir_.bind_names() - .Get(inst.As().bind_name_id) + .Get(inst.As().bind_name_id) .name_id); continue; } diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index b84c83317aa4..0aa7274881b4 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -255,9 +255,8 @@ class Inst : public Printable { }; // TODO: This is currently 20 bytes because we sometimes have 2 arguments for a -// pair of Insts. However, InstKind is 1 byte; if args -// were 3.5 bytes, we could potentially shrink Inst by 4 bytes. This -// may be worth investigating further. +// pair of Insts. However, InstKind is 1 byte; if args were 3.5 bytes, we could +// potentially shrink Inst by 4 bytes. This may be worth investigating further. static_assert(sizeof(Inst) == 20, "Unexpected Inst size"); // Instruction-like types can be printed by converting them to instructions. diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 617549d33669..3a7c3e7da2b5 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -24,6 +24,7 @@ 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(BindName) CARBON_SEM_IR_INST_KIND(BindValue) CARBON_SEM_IR_INST_KIND(BlockArg) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 2b1788eb4a57..4537771a914f 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -64,7 +64,7 @@ struct AddrPattern { Parse::AddressId parse_node; TypeId type_id; - // The `self` parameter. + // The `self` binding. InstId inner_id; }; @@ -125,6 +125,29 @@ struct BaseDecl { ElementIndex index; }; +// Common representation for both kinds of `bind*name` node. +struct AnyBindName { + // TODO: Also handle BindTemplateName once it exists. + static constexpr InstKind Kinds[] = {InstKind::BindName, + InstKind::BindSymbolicName}; + + InstKind kind; + Parse::NodeId parse_node; + TypeId type_id; + BindNameId bind_name_id; + InstId value_id; +}; + +struct BindSymbolicName { + static constexpr auto Kind = + InstKind::BindSymbolicName.Define("bind_symbolic_name"); + + Parse::NodeId parse_node; + TypeId type_id; + BindNameId bind_name_id; + InstId value_id; +}; + struct BindName { static constexpr auto Kind = InstKind::BindName.Define("bind_name");