Skip to content

Commit 093700b

Browse files
Add support for boolean literals in macros (#6418)
Adding support for macros with boolean literals. Demo: ```c++ // main.carbon library "Main"; import Core library "io"; import Cpp inline ''' #define M_TRUE true '''; fn Run() -> i32 { let a: bool = Cpp.M_TRUE; if (a) { Core.Print(1); } else { Core.Print(0); } return 0; } ``` ``` $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link main.o \--output=demo_carbon $ ./demo_carbon 1 ``` Part of #6303
1 parent 201e408 commit 093700b

File tree

4 files changed

+239
-7
lines changed

4 files changed

+239
-7
lines changed

toolchain/check/cpp/import.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,9 +2244,8 @@ static auto IsIncompleteClass(Context& context, SemIR::NameScopeId scope_id)
22442244
context.classes().Get(class_decl->class_id).self_type_id);
22452245
}
22462246

2247-
// Maps a Clang literal expression to a Carbon constant. Currently supports
2248-
// only integer and floating-point literals.
2249-
// TODO: Add support for the other constant types for which a C++ to Carbon type
2247+
// Maps a Clang literal expression to a Carbon constant.
2248+
// TODO: Add support for all constant types for which a C++ to Carbon type
22502249
// mapping exists.
22512250
static auto MapConstant(Context& context, SemIR::LocId loc_id,
22522251
clang::Expr* expr) -> SemIR::InstId {
@@ -2286,6 +2285,13 @@ static auto MapConstant(Context& context, SemIR::LocId loc_id,
22862285
context,
22872286
MakeImportedLocIdAndInst<SemIR::IntValue>(
22882287
context, imported_loc_id, {.type_id = type_id, .int_id = int_id}));
2288+
} else if (auto* bool_literal = dyn_cast<clang::CXXBoolLiteralExpr>(expr)) {
2289+
inst_id = AddInstInNoBlock(
2290+
context,
2291+
MakeImportedLocIdAndInst<SemIR::BoolLiteral>(
2292+
context, imported_loc_id,
2293+
{.type_id = type_id,
2294+
.value = SemIR::BoolValue::From(bool_literal->getValue())}));
22892295
} else if (auto* float_literal = dyn_cast<clang::FloatingLiteral>(expr)) {
22902296
FloatId float_id = context.floats().Add(float_literal->getValue());
22912297
inst_id = AddInstInNoBlock(context,

toolchain/check/cpp/macros.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id,
7171
clang::APValue ap_value = evaluated_result.Val;
7272
switch (ap_value.getKind()) {
7373
case clang::APValue::Int:
74+
if (result_expr->getType()->isBooleanType()) {
75+
return clang::CXXBoolLiteralExpr::Create(
76+
sema.getASTContext(), ap_value.getInt().getBoolValue(),
77+
result_expr->getType(), result_expr->getExprLoc());
78+
}
7479
return clang::IntegerLiteral::Create(
7580
sema.getASTContext(), ap_value.getInt(), result_expr->getType(),
7681
result_expr->getExprLoc());

toolchain/check/cpp/macros.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
namespace Carbon::Check {
1111

1212
// Tries to evaluate the given macro to a constant expression. Returns the
13-
// evaluated expression on success or nullptr otherwise. Currently supports only
14-
// object-like macros that evaluate to an integer constant.
15-
// TODO: Add support for other literal types.
13+
// evaluated expression on success or nullptr otherwise.
14+
// TODO: Add support for all literal types.
1615
auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id,
1716
SemIR::NameId name_id,
1817
clang::MacroInfo* macro_info) -> clang::Expr*;

toolchain/check/testdata/interop/cpp/macros.carbon

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,60 @@ fn F() {
412412
Cpp.MyDouble = 1.0;
413413
}
414414

415+
// --- boolean_literal_macro.h
416+
#define M_TRUE true
417+
#define M_FALSE false
418+
419+
#define M_ONE 1
420+
#define M_ZERO 0
421+
422+
#define M_NOT_TRUE !true
423+
#define M_NOT_ZERO (!0)
424+
425+
#define M_ONE_EQ_ONE (1 == 1)
426+
#define M_ONE_NEQ_ONE (1 != 1)
427+
#define M_AND (true && true)
428+
#define M_OR (M_TRUE || M_FALSE)
429+
430+
#define M_COMPLEX ((2 * 2 < 5) && !M_FALSE)
431+
432+
// --- import_boolean_literal_macro.carbon
433+
434+
library "[[@TEST_NAME]]";
435+
436+
import Cpp library "boolean_literal_macro.h";
437+
438+
fn F() {
439+
//@dump-sem-ir-begin
440+
let a: bool = Cpp.M_TRUE;
441+
let b: bool = Cpp.M_FALSE;
442+
let c: bool = Cpp.M_NOT_TRUE;
443+
let d: bool = Cpp.M_NOT_ZERO;
444+
let e: bool = Cpp.M_ONE_EQ_ONE;
445+
let f: bool = Cpp.M_ONE_NEQ_ONE;
446+
let g: bool = Cpp.M_AND;
447+
let h: bool = Cpp.M_OR;
448+
let i: bool = Cpp.M_COMPLEX;
449+
450+
let j: i32 = Cpp.M_ONE;
451+
let k: i32 = Cpp.M_ZERO;
452+
//@dump-sem-ir-end
453+
}
454+
455+
// --- fail_assign_to_boolean_literal_macro.carbon
456+
457+
library "[[@TEST_NAME]]";
458+
459+
import Cpp library "boolean_literal_macro.h";
460+
461+
fn F() {
462+
// CHECK:STDERR: fail_assign_to_boolean_literal_macro.carbon:[[@LINE+4]]:2: error: expression is not assignable [AssignmentToNonAssignable]
463+
// CHECK:STDERR: Cpp.M_TRUE = false;
464+
// CHECK:STDERR: ^~~~~~~~~~
465+
// CHECK:STDERR:
466+
Cpp.M_TRUE = false;
467+
}
468+
415469
// --- lambda.carbon
416470

417471
library "[[@TEST_NAME]]";
@@ -424,7 +478,6 @@ fn F() {
424478
let i: i32 = Cpp.MyIntLambda;
425479
}
426480

427-
428481
// --- macro_undefined.h
429482

430483
#define CONFIG_VALUE 1
@@ -908,6 +961,175 @@ fn F() {
908961
// CHECK:STDOUT: <elided>
909962
// CHECK:STDOUT: }
910963
// CHECK:STDOUT:
964+
// CHECK:STDOUT: --- import_boolean_literal_macro.carbon
965+
// CHECK:STDOUT:
966+
// CHECK:STDOUT: constants {
967+
// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete]
968+
// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete]
969+
// CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete]
970+
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
971+
// CHECK:STDOUT: %false: bool = bool_literal false [concrete]
972+
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
973+
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
974+
// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete]
975+
// CHECK:STDOUT: %int_1: %i32 = int_value 1 [concrete]
976+
// CHECK:STDOUT: %int_0: %i32 = int_value 0 [concrete]
977+
// CHECK:STDOUT: }
978+
// CHECK:STDOUT:
979+
// CHECK:STDOUT: imports {
980+
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
981+
// CHECK:STDOUT: .M_TRUE = %true.d1048d.1
982+
// CHECK:STDOUT: .M_FALSE = %false.505718.1
983+
// CHECK:STDOUT: .M_NOT_TRUE = %false.505718.2
984+
// CHECK:STDOUT: .M_NOT_ZERO = %true.d1048d.2
985+
// CHECK:STDOUT: .M_ONE_EQ_ONE = %true.d1048d.3
986+
// CHECK:STDOUT: .M_ONE_NEQ_ONE = %false.505718.3
987+
// CHECK:STDOUT: .M_AND = %true.d1048d.4
988+
// CHECK:STDOUT: .M_OR = %true.d1048d.5
989+
// CHECK:STDOUT: .M_COMPLEX = %true.d1048d.6
990+
// CHECK:STDOUT: .M_ONE = %int_1
991+
// CHECK:STDOUT: .M_ZERO = %int_0
992+
// CHECK:STDOUT: import Cpp//...
993+
// CHECK:STDOUT: }
994+
// CHECK:STDOUT: %true.d1048d.1: bool = bool_literal true [concrete = constants.%true]
995+
// CHECK:STDOUT: %false.505718.1: bool = bool_literal false [concrete = constants.%false]
996+
// CHECK:STDOUT: %false.505718.2: bool = bool_literal false [concrete = constants.%false]
997+
// CHECK:STDOUT: %true.d1048d.2: bool = bool_literal true [concrete = constants.%true]
998+
// CHECK:STDOUT: %true.d1048d.3: bool = bool_literal true [concrete = constants.%true]
999+
// CHECK:STDOUT: %false.505718.3: bool = bool_literal false [concrete = constants.%false]
1000+
// CHECK:STDOUT: %true.d1048d.4: bool = bool_literal true [concrete = constants.%true]
1001+
// CHECK:STDOUT: %true.d1048d.5: bool = bool_literal true [concrete = constants.%true]
1002+
// CHECK:STDOUT: %true.d1048d.6: bool = bool_literal true [concrete = constants.%true]
1003+
// CHECK:STDOUT: %int_1: %i32 = int_value 1 [concrete = constants.%int_1]
1004+
// CHECK:STDOUT: %int_0: %i32 = int_value 0 [concrete = constants.%int_0]
1005+
// CHECK:STDOUT: }
1006+
// CHECK:STDOUT:
1007+
// CHECK:STDOUT: fn @F() {
1008+
// CHECK:STDOUT: !entry:
1009+
// CHECK:STDOUT: name_binding_decl {
1010+
// CHECK:STDOUT: %a.patt: %pattern_type.831 = value_binding_pattern a [concrete]
1011+
// CHECK:STDOUT: }
1012+
// CHECK:STDOUT: %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1013+
// CHECK:STDOUT: %M_TRUE.ref: bool = name_ref M_TRUE, imports.%true.d1048d.1 [concrete = constants.%true]
1014+
// CHECK:STDOUT: %.loc8_10.1: type = splice_block %.loc8_10.3 [concrete = bool] {
1015+
// CHECK:STDOUT: %Bool.call.loc8: init type = call constants.%Bool() [concrete = bool]
1016+
// CHECK:STDOUT: %.loc8_10.2: type = value_of_initializer %Bool.call.loc8 [concrete = bool]
1017+
// CHECK:STDOUT: %.loc8_10.3: type = converted %Bool.call.loc8, %.loc8_10.2 [concrete = bool]
1018+
// CHECK:STDOUT: }
1019+
// CHECK:STDOUT: %a: bool = value_binding a, %M_TRUE.ref
1020+
// CHECK:STDOUT: name_binding_decl {
1021+
// CHECK:STDOUT: %b.patt: %pattern_type.831 = value_binding_pattern b [concrete]
1022+
// CHECK:STDOUT: }
1023+
// CHECK:STDOUT: %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1024+
// CHECK:STDOUT: %M_FALSE.ref: bool = name_ref M_FALSE, imports.%false.505718.1 [concrete = constants.%false]
1025+
// CHECK:STDOUT: %.loc9_10.1: type = splice_block %.loc9_10.3 [concrete = bool] {
1026+
// CHECK:STDOUT: %Bool.call.loc9: init type = call constants.%Bool() [concrete = bool]
1027+
// CHECK:STDOUT: %.loc9_10.2: type = value_of_initializer %Bool.call.loc9 [concrete = bool]
1028+
// CHECK:STDOUT: %.loc9_10.3: type = converted %Bool.call.loc9, %.loc9_10.2 [concrete = bool]
1029+
// CHECK:STDOUT: }
1030+
// CHECK:STDOUT: %b: bool = value_binding b, %M_FALSE.ref
1031+
// CHECK:STDOUT: name_binding_decl {
1032+
// CHECK:STDOUT: %c.patt: %pattern_type.831 = value_binding_pattern c [concrete]
1033+
// CHECK:STDOUT: }
1034+
// CHECK:STDOUT: %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1035+
// CHECK:STDOUT: %M_NOT_TRUE.ref: bool = name_ref M_NOT_TRUE, imports.%false.505718.2 [concrete = constants.%false]
1036+
// CHECK:STDOUT: %.loc10_10.1: type = splice_block %.loc10_10.3 [concrete = bool] {
1037+
// CHECK:STDOUT: %Bool.call.loc10: init type = call constants.%Bool() [concrete = bool]
1038+
// CHECK:STDOUT: %.loc10_10.2: type = value_of_initializer %Bool.call.loc10 [concrete = bool]
1039+
// CHECK:STDOUT: %.loc10_10.3: type = converted %Bool.call.loc10, %.loc10_10.2 [concrete = bool]
1040+
// CHECK:STDOUT: }
1041+
// CHECK:STDOUT: %c: bool = value_binding c, %M_NOT_TRUE.ref
1042+
// CHECK:STDOUT: name_binding_decl {
1043+
// CHECK:STDOUT: %d.patt: %pattern_type.831 = value_binding_pattern d [concrete]
1044+
// CHECK:STDOUT: }
1045+
// CHECK:STDOUT: %Cpp.ref.loc11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1046+
// CHECK:STDOUT: %M_NOT_ZERO.ref: bool = name_ref M_NOT_ZERO, imports.%true.d1048d.2 [concrete = constants.%true]
1047+
// CHECK:STDOUT: %.loc11_10.1: type = splice_block %.loc11_10.3 [concrete = bool] {
1048+
// CHECK:STDOUT: %Bool.call.loc11: init type = call constants.%Bool() [concrete = bool]
1049+
// CHECK:STDOUT: %.loc11_10.2: type = value_of_initializer %Bool.call.loc11 [concrete = bool]
1050+
// CHECK:STDOUT: %.loc11_10.3: type = converted %Bool.call.loc11, %.loc11_10.2 [concrete = bool]
1051+
// CHECK:STDOUT: }
1052+
// CHECK:STDOUT: %d: bool = value_binding d, %M_NOT_ZERO.ref
1053+
// CHECK:STDOUT: name_binding_decl {
1054+
// CHECK:STDOUT: %e.patt: %pattern_type.831 = value_binding_pattern e [concrete]
1055+
// CHECK:STDOUT: }
1056+
// CHECK:STDOUT: %Cpp.ref.loc12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1057+
// CHECK:STDOUT: %M_ONE_EQ_ONE.ref: bool = name_ref M_ONE_EQ_ONE, imports.%true.d1048d.3 [concrete = constants.%true]
1058+
// CHECK:STDOUT: %.loc12_10.1: type = splice_block %.loc12_10.3 [concrete = bool] {
1059+
// CHECK:STDOUT: %Bool.call.loc12: init type = call constants.%Bool() [concrete = bool]
1060+
// CHECK:STDOUT: %.loc12_10.2: type = value_of_initializer %Bool.call.loc12 [concrete = bool]
1061+
// CHECK:STDOUT: %.loc12_10.3: type = converted %Bool.call.loc12, %.loc12_10.2 [concrete = bool]
1062+
// CHECK:STDOUT: }
1063+
// CHECK:STDOUT: %e: bool = value_binding e, %M_ONE_EQ_ONE.ref
1064+
// CHECK:STDOUT: name_binding_decl {
1065+
// CHECK:STDOUT: %f.patt: %pattern_type.831 = value_binding_pattern f [concrete]
1066+
// CHECK:STDOUT: }
1067+
// CHECK:STDOUT: %Cpp.ref.loc13: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1068+
// CHECK:STDOUT: %M_ONE_NEQ_ONE.ref: bool = name_ref M_ONE_NEQ_ONE, imports.%false.505718.3 [concrete = constants.%false]
1069+
// CHECK:STDOUT: %.loc13_10.1: type = splice_block %.loc13_10.3 [concrete = bool] {
1070+
// CHECK:STDOUT: %Bool.call.loc13: init type = call constants.%Bool() [concrete = bool]
1071+
// CHECK:STDOUT: %.loc13_10.2: type = value_of_initializer %Bool.call.loc13 [concrete = bool]
1072+
// CHECK:STDOUT: %.loc13_10.3: type = converted %Bool.call.loc13, %.loc13_10.2 [concrete = bool]
1073+
// CHECK:STDOUT: }
1074+
// CHECK:STDOUT: %f: bool = value_binding f, %M_ONE_NEQ_ONE.ref
1075+
// CHECK:STDOUT: name_binding_decl {
1076+
// CHECK:STDOUT: %g.patt: %pattern_type.831 = value_binding_pattern g [concrete]
1077+
// CHECK:STDOUT: }
1078+
// CHECK:STDOUT: %Cpp.ref.loc14: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1079+
// CHECK:STDOUT: %M_AND.ref: bool = name_ref M_AND, imports.%true.d1048d.4 [concrete = constants.%true]
1080+
// CHECK:STDOUT: %.loc14_10.1: type = splice_block %.loc14_10.3 [concrete = bool] {
1081+
// CHECK:STDOUT: %Bool.call.loc14: init type = call constants.%Bool() [concrete = bool]
1082+
// CHECK:STDOUT: %.loc14_10.2: type = value_of_initializer %Bool.call.loc14 [concrete = bool]
1083+
// CHECK:STDOUT: %.loc14_10.3: type = converted %Bool.call.loc14, %.loc14_10.2 [concrete = bool]
1084+
// CHECK:STDOUT: }
1085+
// CHECK:STDOUT: %g: bool = value_binding g, %M_AND.ref
1086+
// CHECK:STDOUT: name_binding_decl {
1087+
// CHECK:STDOUT: %h.patt: %pattern_type.831 = value_binding_pattern h [concrete]
1088+
// CHECK:STDOUT: }
1089+
// CHECK:STDOUT: %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1090+
// CHECK:STDOUT: %M_OR.ref: bool = name_ref M_OR, imports.%true.d1048d.5 [concrete = constants.%true]
1091+
// CHECK:STDOUT: %.loc15_10.1: type = splice_block %.loc15_10.3 [concrete = bool] {
1092+
// CHECK:STDOUT: %Bool.call.loc15: init type = call constants.%Bool() [concrete = bool]
1093+
// CHECK:STDOUT: %.loc15_10.2: type = value_of_initializer %Bool.call.loc15 [concrete = bool]
1094+
// CHECK:STDOUT: %.loc15_10.3: type = converted %Bool.call.loc15, %.loc15_10.2 [concrete = bool]
1095+
// CHECK:STDOUT: }
1096+
// CHECK:STDOUT: %h: bool = value_binding h, %M_OR.ref
1097+
// CHECK:STDOUT: name_binding_decl {
1098+
// CHECK:STDOUT: %i.patt: %pattern_type.831 = value_binding_pattern i [concrete]
1099+
// CHECK:STDOUT: }
1100+
// CHECK:STDOUT: %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1101+
// CHECK:STDOUT: %M_COMPLEX.ref: bool = name_ref M_COMPLEX, imports.%true.d1048d.6 [concrete = constants.%true]
1102+
// CHECK:STDOUT: %.loc16_10.1: type = splice_block %.loc16_10.3 [concrete = bool] {
1103+
// CHECK:STDOUT: %Bool.call.loc16: init type = call constants.%Bool() [concrete = bool]
1104+
// CHECK:STDOUT: %.loc16_10.2: type = value_of_initializer %Bool.call.loc16 [concrete = bool]
1105+
// CHECK:STDOUT: %.loc16_10.3: type = converted %Bool.call.loc16, %.loc16_10.2 [concrete = bool]
1106+
// CHECK:STDOUT: }
1107+
// CHECK:STDOUT: %i: bool = value_binding i, %M_COMPLEX.ref
1108+
// CHECK:STDOUT: name_binding_decl {
1109+
// CHECK:STDOUT: %j.patt: %pattern_type.7ce = value_binding_pattern j [concrete]
1110+
// CHECK:STDOUT: }
1111+
// CHECK:STDOUT: %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1112+
// CHECK:STDOUT: <elided>
1113+
// CHECK:STDOUT: %M_ONE.ref: %i32 = name_ref M_ONE, imports.%int_1 [concrete = constants.%int_1]
1114+
// CHECK:STDOUT: %.loc18: type = splice_block %i32.loc18 [concrete = constants.%i32] {
1115+
// CHECK:STDOUT: %int_32.loc18: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
1116+
// CHECK:STDOUT: %i32.loc18: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
1117+
// CHECK:STDOUT: }
1118+
// CHECK:STDOUT: %j: %i32 = value_binding j, %M_ONE.ref
1119+
// CHECK:STDOUT: name_binding_decl {
1120+
// CHECK:STDOUT: %k.patt: %pattern_type.7ce = value_binding_pattern k [concrete]
1121+
// CHECK:STDOUT: }
1122+
// CHECK:STDOUT: %Cpp.ref.loc19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1123+
// CHECK:STDOUT: <elided>
1124+
// CHECK:STDOUT: %M_ZERO.ref: %i32 = name_ref M_ZERO, imports.%int_0 [concrete = constants.%int_0]
1125+
// CHECK:STDOUT: %.loc19: type = splice_block %i32.loc19 [concrete = constants.%i32] {
1126+
// CHECK:STDOUT: %int_32.loc19: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
1127+
// CHECK:STDOUT: %i32.loc19: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
1128+
// CHECK:STDOUT: }
1129+
// CHECK:STDOUT: %k: %i32 = value_binding k, %M_ZERO.ref
1130+
// CHECK:STDOUT: <elided>
1131+
// CHECK:STDOUT: }
1132+
// CHECK:STDOUT:
9111133
// CHECK:STDOUT: --- import_macro_redefined.carbon
9121134
// CHECK:STDOUT:
9131135
// CHECK:STDOUT: constants {

0 commit comments

Comments
 (0)