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");