Просмотр исходного кода

Add initial support for non-type template parameters (#6740)

This adds basic support for using templates with integer parameters.

https://github.com/carbon-language/carbon-lang/issues/6717
Nicholas Bishop 2 месяцев назад
Родитель
Сommit
6df9d5ba32

+ 45 - 4
toolchain/check/cpp/call.cpp

@@ -114,10 +114,51 @@ static auto ConvertArgToTemplateArg(Context& context,
     return MakePlaceholderTemplateArg(context, arg_id);
   }
 
-  if (isa<clang::NonTypeTemplateParmDecl>(param_decl)) {
-    // TODO: Check the argument has a concrete constant value, and convert it to
-    // a Clang constant value.
-    context.TODO(arg_id, "argument for non-type template parameter");
+  if (const auto* non_type =
+          dyn_cast<clang::NonTypeTemplateParmDecl>(param_decl)) {
+    auto param_type = non_type->getType();
+
+    // Handle integer parameters.
+    if (param_type->isIntegerType()) {
+      // Get the Carbon type corresponding to the parameter's Clang type.
+      const auto type_expr =
+          ImportCppType(context, SemIR::LocId(arg_id), param_type);
+
+      // Try to convert the argument to the parameter type.
+      const auto converted_inst_id =
+          Convert(context, SemIR::LocId(arg_id), arg_id,
+                  {
+                      .kind = ConversionTarget::Value,
+                      .type_id = type_expr.type_id,
+                  });
+
+      if (converted_inst_id == SemIR::ErrorInst::InstId) {
+        return std::nullopt;
+      }
+
+      auto const_inst_id =
+          context.constant_values().GetConstantInstId(converted_inst_id);
+      if (const_inst_id.has_value()) {
+        if (auto int_value =
+                context.insts().TryGetAs<SemIR::IntValue>(const_inst_id)) {
+          const auto& ap_int = context.ints().Get(int_value->int_id);
+          const bool is_unsigned =
+              !param_type->isSignedIntegerOrEnumerationType();
+          auto aps_int =
+              llvm::APSInt(ap_int, is_unsigned)
+                  .extOrTrunc(context.ast_context().getIntWidth(param_type));
+          auto template_arg = clang::TemplateArgument(context.ast_context(),
+                                                      aps_int, param_type);
+          // TODO: provide a better location.
+          auto loc = clang::TemplateArgumentLocInfo();
+          return clang::TemplateArgumentLoc(template_arg, loc);
+        }
+      }
+    }
+
+    // TODO: Support other types.
+    context.TODO(arg_id,
+                 "unsupported argument type for non-type template parameter");
     return std::nullopt;
   }
 

+ 67 - 17
toolchain/check/testdata/interop/cpp/template/non_type_param.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -14,20 +14,62 @@
 
 struct A {};
 
+template<int N> struct Simple {};
+Simple<123> MakeSimple123() { return Simple<123>(); }
+
 template<int M, int N> struct TwoNonType {};
 template<typename T, T N> struct DependentNonType {};
 
-// --- fail_todo_valid.carbon
+// --- valid_int_param.carbon
 
 library "[[@TEST_NAME]]";
 
 import Cpp library "templates.h";
 
-//@dump-sem-ir-begin
-// CHECK:STDERR: fail_todo_valid.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo]
-// CHECK:STDERR: var x: Cpp.TwoNonType(1, 2);
-// CHECK:STDERR:                       ^
+var s: Cpp.Simple(123) = Cpp.MakeSimple123();
+
+// --- fail_int_param_mismatch.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "templates.h";
+
+// CHECK:STDERR: fail_int_param_mismatch.carbon:[[@LINE+7]]:1: error: cannot implicitly convert expression of type `Cpp.Simple` to `Cpp.Simple` [ConversionFailure]
+// CHECK:STDERR: var s: Cpp.Simple(456) = Cpp.MakeSimple123();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_int_param_mismatch.carbon:[[@LINE+4]]:1: note: type `Cpp.Simple` does not implement interface `Core.ImplicitAs(Cpp.Simple)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var s: Cpp.Simple(456) = Cpp.MakeSimple123();
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
+var s: Cpp.Simple(456) = Cpp.MakeSimple123();
+
+// --- nonliteral.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "templates.h";
+
+var s: Cpp.Simple(100 + 20 + 3) = Cpp.MakeSimple123();
+
+// --- conversion.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "templates.h";
+
+class C { adapt {}; }
+impl C as Core.ImplicitAs(i32) {
+  eval fn Convert[self: Self]() -> i32 { return 123; }
+}
+var s: Cpp.Simple({} as C) = Cpp.MakeSimple123();
+
+// --- valid.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "templates.h";
+
+//@dump-sem-ir-begin
 var x: Cpp.TwoNonType(1, 2);
 //@dump-sem-ir-end
 
@@ -40,13 +82,13 @@ import Cpp library "templates.h";
 var n: i32;
 
 //@dump-sem-ir-begin
-// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:34: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo]
+// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:34: error: semantics TODO: `unsupported argument type for non-type template parameter` [SemanticsTodo]
 // CHECK:STDERR: var x: Cpp.DependentNonType(i32, 1);
 // CHECK:STDERR:                                  ^
 // CHECK:STDERR:
 var x: Cpp.DependentNonType(i32, 1);
 
-// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:35: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo]
+// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:35: error: semantics TODO: `unsupported argument type for non-type template parameter` [SemanticsTodo]
 // CHECK:STDERR: var y: Cpp.DependentNonType(i32*, &n);
 // CHECK:STDERR:                                   ^~
 // CHECK:STDERR:
@@ -59,9 +101,12 @@ library "[[@TEST_NAME]]";
 
 import Cpp library "templates.h";
 
-// CHECK:STDERR: fail_type_argument.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo]
+// CHECK:STDERR: fail_type_argument.carbon:[[@LINE+7]]:26: error: cannot implicitly convert expression of type `type` to `i32` [ConversionFailure]
 // CHECK:STDERR: var x: Cpp.TwoNonType(1, Cpp.A);
-// CHECK:STDERR:                       ^
+// CHECK:STDERR:                          ^~~~~
+// CHECK:STDERR: fail_type_argument.carbon:[[@LINE+4]]:26: note: type `type` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote]
+// CHECK:STDERR: var x: Cpp.TwoNonType(1, Cpp.A);
+// CHECK:STDERR:                          ^~~~~
 // CHECK:STDERR:
 var x: Cpp.TwoNonType(1, Cpp.A);
 
@@ -73,15 +118,20 @@ import Cpp library "templates.h";
 
 var n: i32;
 
-// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo]
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+7]]:26: error: cannot implicitly convert expression of type `i32*` to `i32` [ConversionFailure]
+// CHECK:STDERR: var x: Cpp.TwoNonType(1, &n);
+// CHECK:STDERR:                          ^~
+// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:26: note: type `i32*` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessNote]
 // CHECK:STDERR: var x: Cpp.TwoNonType(1, &n);
-// CHECK:STDERR:                       ^
+// CHECK:STDERR:                          ^~
 // CHECK:STDERR:
 var x: Cpp.TwoNonType(1, &n);
 
-// CHECK:STDOUT: --- fail_todo_valid.carbon
+// CHECK:STDOUT: --- valid.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %TwoNonType: type = class_type @TwoNonType [concrete]
+// CHECK:STDOUT:   %pattern_type.8a6: type = pattern_type %TwoNonType [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -89,11 +139,11 @@ var x: Cpp.TwoNonType(1, &n);
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %x.patt: <error> = ref_binding_pattern x [concrete]
-// CHECK:STDOUT:     %x.var_patt: <error> = var_pattern %x.patt [concrete]
+// CHECK:STDOUT:     %x.patt: %pattern_type.8a6 = ref_binding_pattern x [concrete]
+// CHECK:STDOUT:     %x.var_patt: %pattern_type.8a6 = var_pattern %x.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %x.var: ref <error> = var %x.var_patt [concrete = <error>]
-// CHECK:STDOUT:   %x: ref <error> = ref_binding x, <error> [concrete = <error>]
+// CHECK:STDOUT:   %x.var: ref %TwoNonType = var %x.var_patt [concrete]
+// CHECK:STDOUT:   %x: ref %TwoNonType = ref_binding x, %x.var [concrete = %x.var]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_dependent.carbon