Răsfoiți Sursa

Add support for macros with floating-point literals (#6391)

Adding support for floating-point literals in macros.

Part of #6303
Ivana Ivanovska 5 luni în urmă
părinte
comite
994e6c904d

+ 33 - 14
toolchain/check/cpp/import.cpp

@@ -30,7 +30,9 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
+#include "toolchain/base/int.h"
 #include "toolchain/base/kind_switch.h"
+#include "toolchain/base/value_ids.h"
 #include "toolchain/check/call.h"
 #include "toolchain/check/class.h"
 #include "toolchain/check/context.h"
@@ -2224,29 +2226,46 @@ static auto IsIncompleteClass(Context& context, SemIR::NameScopeId scope_id)
              context.classes().Get(class_decl->class_id).self_type_id);
 }
 
-// Maps a Clang constant expression to a Carbon constant. Currently supports
-// only integer constants.
+// Maps a Clang literal expression to a Carbon constant. Currently supports
+// only integer and floating-point literals.
 // TODO: Add support for the other constant types for which a C++ to Carbon type
 // mapping exists.
 static auto MapConstant(Context& context, SemIR::LocId loc_id,
                         clang::Expr* expr) -> SemIR::InstId {
   CARBON_CHECK(expr, "empty expression");
-  auto* integer_literal = dyn_cast<clang::IntegerLiteral>(expr);
-  if (!integer_literal) {
-    context.TODO(
-        loc_id, "Unsupported: constant type: " + expr->getType().getAsString());
+
+  SemIR::TypeId type_id = MapType(context, loc_id, expr->getType()).type_id;
+  if (!type_id.has_value()) {
+    context.TODO(loc_id, llvm::formatv("Unsupported: C++ literal's type `{0}` "
+                                       "could not be mapped to a Carbon type",
+                                       expr->getType().getAsString()));
     return SemIR::ErrorInst::InstId;
   }
-  SemIR::TypeId type_id =
-      MapType(context, loc_id, integer_literal->getType()).type_id;
-  if (!type_id.has_value()) {
-    CARBON_DIAGNOSTIC(InCppConstantMapping, Error, "invalid integer type");
-    context.emitter().Emit(loc_id, InCppConstantMapping);
+
+  SemIR::InstId inst_id = SemIR::InstId::None;
+  SemIR::ImportIRInstId imported_loc_id =
+      AddImportIRInst(context.sem_ir(), expr->getExprLoc());
+
+  if (auto* integer_literal = dyn_cast<clang::IntegerLiteral>(expr)) {
+    IntId int_id =
+        context.ints().Add(integer_literal->getValue().getSExtValue());
+    inst_id = AddInstInNoBlock(
+        context,
+        MakeImportedLocIdAndInst<SemIR::IntValue>(
+            context, imported_loc_id, {.type_id = type_id, .int_id = int_id}));
+  } else if (auto* float_literal = dyn_cast<clang::FloatingLiteral>(expr)) {
+    FloatId float_id = context.floats().Add(float_literal->getValue());
+    inst_id = AddInstInNoBlock(context,
+                               MakeImportedLocIdAndInst<SemIR::FloatValue>(
+                                   context, imported_loc_id,
+                                   {.type_id = type_id, .float_id = float_id}));
+  } else {
+    context.TODO(loc_id, llvm::formatv(
+                             "Unsupported: C++ constant expression type: '{0}'",
+                             expr->getType().getAsString()));
     return SemIR::ErrorInst::InstId;
   }
-  auto int_id = context.ints().Add(integer_literal->getValue().getSExtValue());
-  auto inst_id = AddInstInNoBlock<SemIR::IntValue>(
-      context, loc_id, {.type_id = type_id, .int_id = int_id});
+
   context.imports().push_back(inst_id);
   return inst_id;
 }

+ 21 - 8
toolchain/check/cpp/macros.cpp

@@ -8,6 +8,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Sema/Sema.h"
+#include "common/check.h"
 
 namespace Carbon::Check {
 
@@ -40,7 +41,8 @@ auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id,
 
   tokens.push_back(current_token);
 
-  preprocessor.EnterTokenStream(tokens, false, false);
+  preprocessor.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false,
+                                /*IsReinject=*/false);
   parser.ConsumeAnyToken(true);
 
   clang::ExprResult result = parser.ParseConstantExpression();
@@ -53,20 +55,31 @@ auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id,
     parser.SkipUntil(clang::tok::eof);
     CARBON_DIAGNOSTIC(
         InCppMacroEvaluation, Error,
-        "failed to evaluate macro Cpp.{0} to a valid constant expression",
+        "failed to parse macro Cpp.{0} to a valid constant expression",
         std::string);
     context.emitter().Emit(loc_id, InCppMacroEvaluation, (*name_str_opt).str());
     return nullptr;
   }
 
   clang::Expr::EvalResult evaluated_result;
-  if (!result_expr->EvaluateAsInt(evaluated_result, sema.getASTContext())) {
-    context.TODO(loc_id, "non-integer constant expression in macro.");
-    return nullptr;
+  CARBON_CHECK(result_expr->EvaluateAsConstantExpr(evaluated_result,
+                                                   sema.getASTContext()));
+  clang::APValue ap_value = evaluated_result.Val;
+  switch (ap_value.getKind()) {
+    case clang::APValue::Int:
+      return clang::IntegerLiteral::Create(
+          sema.getASTContext(), ap_value.getInt(), result_expr->getType(),
+          result_expr->getExprLoc());
+    case clang::APValue::Float:
+      return clang::FloatingLiteral::Create(
+          sema.getASTContext(), ap_value.getFloat(),
+          /*isExact=*/true, result_expr->getType(), result_expr->getExprLoc());
+    default:
+      context.TODO(loc_id,
+                   "Unsupported: macro evaluated to a constant of type: " +
+                       result_expr->getType().getAsString());
+      return nullptr;
   }
-  return clang::IntegerLiteral::Create(
-      sema.getASTContext(), evaluated_result.Val.getInt(),
-      result_expr->getType(), result_expr->getExprLoc());
 }
 
 }  // namespace Carbon::Check

+ 125 - 16
toolchain/check/testdata/interop/cpp/macros.carbon

@@ -57,7 +57,7 @@ fn F() {
   // CHECK:STDERR:   Cpp.CONFIG_VALUE;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+11]]:3: error: failed to evaluate macro Cpp.CONFIG_VALUE to a valid constant expression [InCppMacroEvaluation]
+  // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+11]]:3: error: failed to parse macro Cpp.CONFIG_VALUE to a valid constant expression [InCppMacroEvaluation]
   // CHECK:STDERR:   Cpp.CONFIG_VALUE;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup]
@@ -89,7 +89,7 @@ fn F() {
   // CHECK:STDERR:   Cpp.CONFIG_VALUE;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  // CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+11]]:3: error: invalid integer type [InCppConstantMapping]
+  // CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: C++ literal's type `unsigned long long` could not be mapped to a Carbon type` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.CONFIG_VALUE;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup]
@@ -264,7 +264,7 @@ import Cpp library "string_literal_object_like_macro.h";
 
 fn F() {
   // TODO: Get rid of the second error.
-  // CHECK:STDERR: fail_todo_import_string_literal_object_like_macro.carbon:[[@LINE+11]]:3: error: semantics TODO: `non-integer constant expression in macro.` [SemanticsTodo]
+  // CHECK:STDERR: fail_todo_import_string_literal_object_like_macro.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: macro evaluated to a constant of type: const char[4]` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.CONFIG_VALUE;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_todo_import_string_literal_object_like_macro.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup]
@@ -279,30 +279,75 @@ fn F() {
 }
 
 // --- floating_point_literal_macro.h
-#define PI 3.14
+#define MyDouble 1.0
+#define MyDoubleE 1e2
+#define MyFloat 1.f
 
-// --- fail_todo_floating_point_literal_macro.carbon
+// --- import_floating_point_literal_macro.carbon
 
 library "[[@TEST_NAME]]";
 
 import Cpp library "floating_point_literal_macro.h";
 
 fn F() {
-  // TODO: Get rid of the second error.
-  // CHECK:STDERR: fail_todo_floating_point_literal_macro.carbon:[[@LINE+11]]:16: error: semantics TODO: `non-integer constant expression in macro.` [SemanticsTodo]
-  // CHECK:STDERR:   let a: f64 = Cpp.PI;
-  // CHECK:STDERR:                ^~~~~~
-  // CHECK:STDERR: fail_todo_floating_point_literal_macro.carbon:[[@LINE+8]]:16: note: in `Cpp` name lookup for `PI` [InCppNameLookup]
-  // CHECK:STDERR:   let a: f64 = Cpp.PI;
-  // CHECK:STDERR:                ^~~~~~
+  //@dump-sem-ir-begin
+  let a: f64 = Cpp.MyDouble;
+  let b: f64 = Cpp.MyDoubleE;
+  let c: f32 = Cpp.MyFloat;
+  //@dump-sem-ir-end
+}
+
+// --- unsupported_floating_point_literal_macro.h
+#define MyLongDouble 987.654l
+
+// --- fail_import_unsupported_floating_point_literal_macro.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "unsupported_floating_point_literal_macro.h";
+
+fn F() {
+  // CHECK:STDERR: fail_import_unsupported_floating_point_literal_macro.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: C++ literal's type `long double` could not be mapped to a Carbon type` [SemanticsTodo]
+  // CHECK:STDERR:   Cpp.MyLongDouble;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_unsupported_floating_point_literal_macro.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `MyLongDouble` [InCppNameLookup]
+  // CHECK:STDERR:   Cpp.MyLongDouble;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  // CHECK:STDERR: fail_todo_floating_point_literal_macro.carbon:[[@LINE+4]]:16: error: member name `PI` not found in `Cpp` [MemberNameNotFoundInInstScope]
-  // CHECK:STDERR:   let a: f64 = Cpp.PI;
-  // CHECK:STDERR:                ^~~~~~
+  // CHECK:STDERR: fail_import_unsupported_floating_point_literal_macro.carbon:[[@LINE+4]]:3: error: member name `MyLongDouble` not found in `Cpp` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   Cpp.MyLongDouble;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  let a: f64 = Cpp.PI;
+  Cpp.MyLongDouble;
 }
 
+// --- fail_import_assign_to_float.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "floating_point_literal_macro.h";
+
+fn F() {
+  // CHECK:STDERR: fail_import_assign_to_float.carbon:[[@LINE+4]]:3: error: expression is not assignable [AssignmentToNonAssignable]
+  // CHECK:STDERR:   Cpp.MyDouble = 1.0;
+  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.MyDouble = 1.0;
+}
+
+// --- lambda.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp inline '''
+  #define MyIntLambda (([] { return 7; })())
+''';
+
+fn F() {
+  let i: i32 = Cpp.MyIntLambda;
+}
+
+
 // --- macro_undefined.h
 
 #define CONFIG_VALUE 1
@@ -722,6 +767,70 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- import_floating_point_literal_macro.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
+// CHECK:STDOUT:   %f64.d77: type = class_type @Float, @Float(%int_64) [concrete]
+// CHECK:STDOUT:   %pattern_type.0ae: type = pattern_type %f64.d77 [concrete]
+// CHECK:STDOUT:   %float.d20: %f64.d77 = float_value 1 [concrete]
+// CHECK:STDOUT:   %float.e0c: %f64.d77 = float_value 100 [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %f32.97e: type = class_type @Float, @Float(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.201: type = pattern_type %f32.97e [concrete]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .MyDouble = %float.d20
+// CHECK:STDOUT:     .MyDoubleE = %float.e0c
+// CHECK:STDOUT:     .MyFloat = %float.e3b
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %float.d20: %f64.d77 = float_value 1 [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %float.e0c: %f64.d77 = float_value 100 [concrete = constants.%float.e0c]
+// CHECK:STDOUT:   %float.e3b: %f32.97e = float_value 1 [concrete = constants.%float.e3b]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %a.patt: %pattern_type.0ae = value_binding_pattern a [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %MyDouble.ref: %f64.d77 = name_ref MyDouble, imports.%float.d20 [concrete = constants.%float.d20]
+// CHECK:STDOUT:   %.loc8: type = splice_block %f64.loc8 [concrete = constants.%f64.d77] {
+// CHECK:STDOUT:     %int_64.loc8: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
+// CHECK:STDOUT:     %f64.loc8: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %a: %f64.d77 = value_binding a, %MyDouble.ref
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %b.patt: %pattern_type.0ae = value_binding_pattern b [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %MyDoubleE.ref: %f64.d77 = name_ref MyDoubleE, imports.%float.e0c [concrete = constants.%float.e0c]
+// CHECK:STDOUT:   %.loc9: type = splice_block %f64.loc9 [concrete = constants.%f64.d77] {
+// CHECK:STDOUT:     %int_64.loc9: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
+// CHECK:STDOUT:     %f64.loc9: type = class_type @Float, @Float(constants.%int_64) [concrete = constants.%f64.d77]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %b: %f64.d77 = value_binding b, %MyDoubleE.ref
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %c.patt: %pattern_type.201 = value_binding_pattern c [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %MyFloat.ref: %f32.97e = name_ref MyFloat, imports.%float.e3b [concrete = constants.%float.e3b]
+// CHECK:STDOUT:   %.loc10: type = splice_block %f32.loc10 [concrete = constants.%f32.97e] {
+// CHECK:STDOUT:     %int_32.loc10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %f32.loc10: type = class_type @Float, @Float(constants.%int_32) [concrete = constants.%f32.97e]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %c: %f32.97e = value_binding c, %MyFloat.ref
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_macro_redefined.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 0 - 1
toolchain/diagnostics/diagnostic_kind.def

@@ -378,7 +378,6 @@ CARBON_DIAGNOSTIC_KIND(QualifiedDeclInIncompleteClassScope)
 CARBON_DIAGNOSTIC_KIND(QualifiedDeclInUndefinedInterfaceScope)
 
 // Name lookup.
-CARBON_DIAGNOSTIC_KIND(InCppConstantMapping)
 CARBON_DIAGNOSTIC_KIND(InCppMacroEvaluation)
 CARBON_DIAGNOSTIC_KIND(InCppNameLookup)
 CARBON_DIAGNOSTIC_KIND(InNameLookup)