From 3b0dad9dd55b7306ec5d15786f3b07b938cada2d Mon Sep 17 00:00:00 2001 From: Ivana Ivanovska Date: Tue, 11 Nov 2025 12:24:20 +0100 Subject: [PATCH] Add support for simple object-like macros (#6326) Adds support for object-like macros with a single replacement numeric-literal kind token. Only macros that evaluate to an integer constant are supported for now. When detected at name lookup, they are imported as a constant integer value in Carbon. Demo: ```c++ // --- macros.h #define CONFIG_VALUE 2 ``` ``` c++ // main.carbon library "Main"; import Cpp library "macros.h"; import Core library "io"; fn Run() { let a: i32 = Cpp.CONFIG_VALUE; Core.Print(a); } ``` ```c++ $ bazel-bin/toolchain/carbon compile main.carbon $ bazel-bin/toolchain/carbon link main.o \--output=demo_carbon $ ./demo_carbon 2 ``` Part of #6303 --- toolchain/check/BUILD | 2 + toolchain/check/cpp/import.cpp | 86 +++ toolchain/check/cpp/macros.cpp | 46 ++ toolchain/check/cpp/macros.h | 23 + .../check/testdata/interop/cpp/macros.carbon | 502 ++++++++++++++++++ toolchain/diagnostics/diagnostic_kind.def | 2 + 6 files changed, 661 insertions(+) create mode 100644 toolchain/check/cpp/macros.cpp create mode 100644 toolchain/check/cpp/macros.h create mode 100644 toolchain/check/testdata/interop/cpp/macros.carbon diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 3fea8437120f..0dfd69cffc43 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -26,6 +26,7 @@ cc_library( "cpp/custom_type_mapping.cpp", "cpp/import.cpp", "cpp/location.cpp", + "cpp/macros.cpp", "cpp/operators.cpp", "cpp/overload_resolution.cpp", "cpp/thunk.cpp", @@ -78,6 +79,7 @@ cc_library( "cpp/custom_type_mapping.h", "cpp/import.h", "cpp/location.h", + "cpp/macros.h", "cpp/operators.h", "cpp/overload_resolution.h", "cpp/thunk.h", diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index cef23fdb3ad1..e0ab4ff76d30 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -27,6 +27,7 @@ #include "common/ostream.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" #include "toolchain/base/kind_switch.h" @@ -37,6 +38,7 @@ #include "toolchain/check/convert.h" #include "toolchain/check/cpp/access.h" #include "toolchain/check/cpp/custom_type_mapping.h" +#include "toolchain/check/cpp/macros.h" #include "toolchain/check/cpp/thunk.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/eval.h" @@ -2252,6 +2254,86 @@ 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. +// 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(expr); + if (!integer_literal) { + context.TODO( + loc_id, "Unsupported: constant 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); + return SemIR::ErrorInst::InstId; + } + auto int_id = context.ints().Add(integer_literal->getValue().getSExtValue()); + auto inst_id = AddInstInNoBlock( + context, loc_id, {.type_id = type_id, .int_id = int_id}); + context.imports().push_back(inst_id); + return inst_id; +} + +// Imports a macro definition into the scope. Currently supports only simple +// object-like macros that expand to a constant integer value. +// TODO: Add support for other macro types and non-integer literal values. +static auto ImportMacro(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, SemIR::NameId name_id, + clang::MacroInfo* macro_info) + -> SemIR::ScopeLookupResult { + clang::Expr* macro_expr = + TryEvaluateMacroToConstant(context, loc_id, name_id, macro_info); + + if (!macro_expr) { + return SemIR::ScopeLookupResult::MakeNotFound(); + } + + auto inst_id = MapConstant(context, loc_id, macro_expr); + if (inst_id == SemIR::ErrorInst::InstId) { + return SemIR::ScopeLookupResult::MakeNotFound(); + } + + AddNameToScope(context, scope_id, name_id, SemIR::AccessKind::Public, + inst_id); + return SemIR::ScopeLookupResult::MakeWrappedLookupResult( + inst_id, SemIR::AccessKind::Public); +} + +// Looks up a macro definition in the top-level `Cpp` scope. Returns nullptr if +// the macro is not found or the scope is not the top-level `Cpp` scope. +static auto LookupMacro(Context& context, SemIR::NameScopeId scope_id, + SemIR::NameId name_id) -> clang::MacroInfo* { + auto name_str_opt = context.names().GetAsStringIfIdentifier(name_id); + if (!name_str_opt || !IsTopCppScope(context, scope_id)) { + return nullptr; + } + + clang::Preprocessor& preprocessor = context.clang_sema().getPreprocessor(); + // TODO: Do the identifier lookup only once, rather than both here and in + // ClangLookupName. + clang::IdentifierInfo* identifier_info = + preprocessor.getIdentifierInfo(*name_str_opt); + + if (!identifier_info) { + return nullptr; + } + + clang::MacroInfo* macro_info = preprocessor.getMacroInfo(identifier_info); + if (macro_info && !macro_info->isUsedForHeaderGuard() && + !macro_info->isFunctionLike() && !macro_info->isBuiltinMacro()) { + return macro_info; + } + + return nullptr; +} + auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id, SemIR::NameId name_id) -> SemIR::ScopeLookupResult { @@ -2264,6 +2346,9 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, if (IsIncompleteClass(context, scope_id)) { return SemIR::ScopeLookupResult::MakeError(); } + if (clang::MacroInfo* macro_info = LookupMacro(context, scope_id, name_id)) { + return ImportMacro(context, loc_id, scope_id, name_id, macro_info); + } auto lookup = ClangLookupName(context, scope_id, name_id); if (!lookup) { return ImportBuiltinTypesIntoScope(context, loc_id, scope_id, name_id); @@ -2280,6 +2365,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, lookup->getNamingClass(), std::move(overload_set)); } + if (!lookup->isSingleResult()) { // Clang will diagnose ambiguous lookup results for us. if (!lookup->isAmbiguous()) { diff --git a/toolchain/check/cpp/macros.cpp b/toolchain/check/cpp/macros.cpp new file mode 100644 index 000000000000..7072e9c47130 --- /dev/null +++ b/toolchain/check/cpp/macros.cpp @@ -0,0 +1,46 @@ +// 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 + +#include "toolchain/check/cpp/macros.h" + +#include "clang/AST/ASTContext.h" +#include "clang/Sema/Sema.h" + +namespace Carbon::Check { + +auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id, + SemIR::NameId name_id, + clang::MacroInfo* macro_info) -> clang::Expr* { + auto name_str_opt = context.names().GetAsStringIfIdentifier(name_id); + CARBON_CHECK(macro_info, "macro info missing"); + if (macro_info->getNumTokens() != 1) { + context.TODO(loc_id, + llvm::formatv("Unsupported: macro with {0} replacement tokens", + macro_info->getNumTokens())); + return nullptr; + } + const clang::Token& tok = macro_info->getReplacementToken(0); + if (!tok.is(clang::tok::numeric_constant)) { + context.TODO(loc_id, + "Unsupported: macro replacement token kind: " + + std::string(clang::tok::getTokenName(tok.getKind()))); + return nullptr; + } + clang::Sema& sema = context.clang_sema(); + clang::ExprResult result = sema.ActOnNumericConstant(tok); + clang::Expr* result_expr = result.get(); + + if (!result_expr || result.isInvalid()) { + CARBON_DIAGNOSTIC( + InCppMacroEvaluation, Error, + "failed to evaluate macro Cpp.{0} to a valid constant expression", + std::string); + context.emitter().Emit(loc_id, InCppMacroEvaluation, (*name_str_opt).str()); + return nullptr; + } + + return result_expr; +} + +} // namespace Carbon::Check diff --git a/toolchain/check/cpp/macros.h b/toolchain/check/cpp/macros.h new file mode 100644 index 000000000000..286bb019362c --- /dev/null +++ b/toolchain/check/cpp/macros.h @@ -0,0 +1,23 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ +#define CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ + +#include "toolchain/check/context.h" + +namespace Carbon::Check { + +// Tries to evaluate the given macro to a constant expression. Returns the +// evaluated expression on success or nullptr otherwise. Currently supports only +// simple object-like macros with a single replacement token that is a numeric +// literal token. +// TODO: Add support for multiple tokens and other literal types. +auto TryEvaluateMacroToConstant(Context& context, SemIR::LocId loc_id, + SemIR::NameId name_id, + clang::MacroInfo* macro_info) -> clang::Expr*; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_CPP_MACROS_H_ diff --git a/toolchain/check/testdata/interop/cpp/macros.carbon b/toolchain/check/testdata/interop/cpp/macros.carbon new file mode 100644 index 000000000000..bf3b2b1dfa8f --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/macros.carbon @@ -0,0 +1,502 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/macros.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/macros.carbon + +// ============================================================================ +// object-like macros +// ============================================================================ + +// --- integer_literal_replacement_token.h +#define CONFIG_VALUE 42 +#define CONFIG_VALUE_LONG 42l +#define CONFIG_VALUE_UNSIGNED 42u +#define CONFIG_VALUE_HEXA 0xFF +#define CONFIG_VALUE_OCTAL 010 +#define CONFIG_VALUE_BINARY 0b101 + +// --- import_integer_literal_replacement_token.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "integer_literal_replacement_token.h"; + +fn F() { + //@dump-sem-ir-begin + let a: i32 = Cpp.CONFIG_VALUE; + let b: i64 = Cpp.CONFIG_VALUE_LONG; + let c: u32 = Cpp.CONFIG_VALUE_UNSIGNED; + let d: i32 = Cpp.CONFIG_VALUE_HEXA; + let e: i32 = Cpp.CONFIG_VALUE_OCTAL; + let f: i32 = Cpp.CONFIG_VALUE_BINARY; + //@dump-sem-ir-end +} + +// --- bad_suffix.h +#define CONFIG_VALUE 42f + +// --- fail_import_bad_suffix.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./bad_suffix.h:1:24: error: invalid digit 'f' in decimal constant [CppInteropParseError] +// CHECK:STDERR: 1 | #define CONFIG_VALUE 42f +// CHECK:STDERR: | ^ +import Cpp library "bad_suffix.h"; + +fn F() { + // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+15]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup] + // 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: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_import_bad_suffix.carbon:[[@LINE+4]]:3: error: member name `CONFIG_VALUE` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.CONFIG_VALUE; +} + +// --- integer_literal_too_big.h +#define CONFIG_VALUE 18446744073709551616 + +// --- fail_import_integer_literal_too_big.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./integer_literal_too_big.h:1:22: error: integer literal is too large to be represented in any integer type [CppInteropParseError] +// CHECK:STDERR: 1 | #define CONFIG_VALUE 18446744073709551616 +// CHECK:STDERR: | ^ +import Cpp library "integer_literal_too_big.h"; + +fn F() { + // CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+15]]:3: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup] + // 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: 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] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_import_integer_literal_too_big.carbon:[[@LINE+4]]:3: error: member name `CONFIG_VALUE` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.CONFIG_VALUE; +} + +// --- fail_assign_to_object_like_macro.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "integer_literal_replacement_token.h"; + +fn F() { + // CHECK:STDERR: fail_assign_to_object_like_macro.carbon:[[@LINE+4]]:3: error: expression is not assignable [AssignmentToNonAssignable] + // CHECK:STDERR: Cpp.CONFIG_VALUE = 3; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.CONFIG_VALUE = 3; +} + +// --- macro_in_nested_scope.h + +namespace N { + #define CONFIG_VALUE 42 +} + +// --- fail_import_macro_in_nested_scope.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "macro_in_nested_scope.h"; + +fn F() { + let a: i32 = Cpp.CONFIG_VALUE; + + // CHECK:STDERR: fail_import_macro_in_nested_scope.carbon:[[@LINE+4]]:16: error: member name `CONFIG_VALUE` not found in `Cpp.N` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: let b: i32 = Cpp.N.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + let b: i32 = Cpp.N.CONFIG_VALUE; +} + +// --- macro_and_non_macro_same_name.h + +namespace X { + float n; +} +float n; +#define n 1 + +// --- import_macro_and_non_macro_same_name.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "macro_and_non_macro_same_name.h"; + +fn F() { + // When the same macro name and non-macro name exist in the global C++ scope, the macro name is selected. + // Cpp.n is treated as an integer constant with value 1. + var a: array(f32, Cpp.n) = (1.0,); + + let b: f32 = Cpp.X.n; +} + +// --- no_replacement_token.h +#define MACRO_NAME + +// --- fail_import_no_replacement_token.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "no_replacement_token.h"; + +fn F() { + // TODO: Get rid of the second error. + // CHECK:STDERR: fail_import_no_replacement_token.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: macro with 0 replacement tokens` [SemanticsTodo] + // CHECK:STDERR: Cpp.MACRO_NAME; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_no_replacement_token.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `MACRO_NAME` [InCppNameLookup] + // CHECK:STDERR: Cpp.MACRO_NAME; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_import_no_replacement_token.carbon:[[@LINE+4]]:3: error: member name `MACRO_NAME` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: Cpp.MACRO_NAME; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.MACRO_NAME; +} + +// --- multiple_replacement_tokens_object_like_macro.h +#define CONFIG_VALUE 1+2 + +// --- fail_todo_import_multiple_replacement_tokens_object_like_macro.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "multiple_replacement_tokens_object_like_macro.h"; + +fn F() { + // TODO: Get rid of the second error. + // CHECK:STDERR: fail_todo_import_multiple_replacement_tokens_object_like_macro.carbon:[[@LINE+11]]:16: error: semantics TODO: `Unsupported: macro with 3 replacement tokens` [SemanticsTodo] + // CHECK:STDERR: let a: i32 = Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_import_multiple_replacement_tokens_object_like_macro.carbon:[[@LINE+8]]:16: note: in `Cpp` name lookup for `CONFIG_VALUE` [InCppNameLookup] + // CHECK:STDERR: let a: i32 = Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_import_multiple_replacement_tokens_object_like_macro.carbon:[[@LINE+4]]:16: error: member name `CONFIG_VALUE` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: let a: i32 = Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + let a: i32 = Cpp.CONFIG_VALUE; +} + +// --- string_literal_object_like_macro.h +#define CONFIG_VALUE "abc" + +// --- fail_todo_import_string_literal_object_like_macro.carbon + +library "[[@TEST_NAME]]"; + +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: `Unsupported: macro replacement token kind: string_literal` [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] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_import_string_literal_object_like_macro.carbon:[[@LINE+4]]:3: error: member name `CONFIG_VALUE` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.CONFIG_VALUE; +} + +// --- floating_point_literal_macro.h +#define PI 3.14 + +// --- fail_todo_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: `Unsupported: constant type: double` [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: ^~~~~~ + // 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: + let a: f64 = Cpp.PI; +} + +// --- macro_undefined.h + +#define CONFIG_VALUE 1 +#undef CONFIG_VALUE + +// --- fail_macro_undefined.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "macro_undefined.h"; + +fn F() { + // CHECK:STDERR: fail_macro_undefined.carbon:[[@LINE+4]]:15: error: member name `CONFIG_VALUE` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: let a: i32 = Cpp.CONFIG_VALUE; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + let a: i32 = Cpp.CONFIG_VALUE; +} + +// --- macro_redefined.h + +#define CONFIG_VALUE 1 +#undef CONFIG_VALUE + +#define CONFIG_VALUE 2 + +// --- import_macro_redefined.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "macro_redefined.h"; + +fn F() { + let a: i32 = Cpp.CONFIG_VALUE; +} + +// --- macro_defined_twice.h + +#define CONFIG_VALUE 1 +#define CONFIG_VALUE 2 + +// --- import_macro_defined_twice.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: import_macro_defined_twice.carbon:[[@LINE+9]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./macro_defined_twice.h:3:9: warning: 'CONFIG_VALUE' macro redefined [CppInteropParseWarning] +// CHECK:STDERR: 3 | #define CONFIG_VALUE 2 +// CHECK:STDERR: | ^ +// CHECK:STDERR: import_macro_defined_twice.carbon:[[@LINE+5]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./macro_defined_twice.h:2:9: note: previous definition is here [CppInteropParseNote] +// CHECK:STDERR: 2 | #define CONFIG_VALUE 1 +// CHECK:STDERR: | ^ +// CHECK:STDERR: +import Cpp library "macro_defined_twice.h"; + +fn F() { + let a: i32 = Cpp.CONFIG_VALUE; +} + +// ============================================================================ +// function-like macros +// ============================================================================ + +// --- function_like_macros.h + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +// --- fail_todo_import_function_like_macros.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "function_like_macros.h"; + +fn F() { + // CHECK:STDERR: fail_todo_import_function_like_macros.carbon:[[@LINE+4]]:16: error: member name `MAX` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: let a: i32 = Cpp.MAX(1,2); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + let a: i32 = Cpp.MAX(1,2); +} + +// ============================================================================ +// predefined macros +// ============================================================================ + +// --- predefined_macros.h + +auto foo() -> void; + +// --- fail_todo_import_predefined_macros.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "predefined_macros.h"; + +fn F() { + // CHECK:STDERR: fail_todo_import_predefined_macros.carbon:[[@LINE+4]]:23: error: member name `__LINE__` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: let line_num: i32 = Cpp.__LINE__; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + let line_num: i32 = Cpp.__LINE__; +} + +// ============================================================================ +// macros used as header guards +// ============================================================================ + +// --- header_guard_macros.h + +#ifndef HEADER_GUARD_MACRO_H_ +#define HEADER_GUARD_MACRO_H_ + +void foo(); + +#endif // HEADER_GUARD_MACRO_H_ + +// --- fail_import_header_guard_macros.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header_guard_macros.h"; + +fn F() { + // CHECK:STDERR: fail_import_header_guard_macros.carbon:[[@LINE+4]]:3: error: member name `HEADER_GUARD_MACRO_H_` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: Cpp.HEADER_GUARD_MACRO_H_; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.HEADER_GUARD_MACRO_H_; +} + +// CHECK:STDOUT: --- import_integer_literal_replacement_token.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %int_42.c68: %i32 = int_value 42 [concrete] +// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete] +// CHECK:STDOUT: %i64: type = class_type @Int, @Int(%int_64) [concrete] +// CHECK:STDOUT: %pattern_type.95b: type = pattern_type %i64 [concrete] +// CHECK:STDOUT: %int_42.434: %i64 = int_value 42 [concrete] +// CHECK:STDOUT: %u32: type = class_type @UInt, @UInt(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.4a9: type = pattern_type %u32 [concrete] +// CHECK:STDOUT: %int_42.58b: %u32 = int_value 42 [concrete] +// CHECK:STDOUT: %int_255: %i32 = int_value 255 [concrete] +// CHECK:STDOUT: %int_8: %i32 = int_value 8 [concrete] +// CHECK:STDOUT: %int_5: %i32 = int_value 5 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .CONFIG_VALUE = %int_42.c68 +// CHECK:STDOUT: .CONFIG_VALUE_LONG = %int_42.434 +// CHECK:STDOUT: .CONFIG_VALUE_UNSIGNED = %int_42.58b +// CHECK:STDOUT: .CONFIG_VALUE_HEXA = %int_255 +// CHECK:STDOUT: .CONFIG_VALUE_OCTAL = %int_8 +// CHECK:STDOUT: .CONFIG_VALUE_BINARY = %int_5 +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %int_42.c68: %i32 = int_value 42 [concrete = constants.%int_42.c68] +// CHECK:STDOUT: %int_42.434: %i64 = int_value 42 [concrete = constants.%int_42.434] +// CHECK:STDOUT: %int_42.58b: %u32 = int_value 42 [concrete = constants.%int_42.58b] +// CHECK:STDOUT: %int_255: %i32 = int_value 255 [concrete = constants.%int_255] +// CHECK:STDOUT: %int_8: %i32 = int_value 8 [concrete = constants.%int_8] +// CHECK:STDOUT: %int_5: %i32 = int_value 5 [concrete = constants.%int_5] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.7ce = value_binding_pattern a [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc8: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE.ref: %i32 = name_ref CONFIG_VALUE, imports.%int_42.c68 [concrete = constants.%int_42.c68] +// CHECK:STDOUT: %.loc8: type = splice_block %i32.loc8 [concrete = constants.%i32] { +// CHECK:STDOUT: %int_32.loc8: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc8: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %i32 = value_binding a, %CONFIG_VALUE.ref +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.95b = value_binding_pattern b [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc9: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE_LONG.ref: %i64 = name_ref CONFIG_VALUE_LONG, imports.%int_42.434 [concrete = constants.%int_42.434] +// CHECK:STDOUT: %.loc9: type = splice_block %i64.loc9 [concrete = constants.%i64] { +// CHECK:STDOUT: %int_64.loc9: Core.IntLiteral = int_value 64 [concrete = constants.%int_64] +// CHECK:STDOUT: %i64.loc9: type = class_type @Int, @Int(constants.%int_64) [concrete = constants.%i64] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: %i64 = value_binding b, %CONFIG_VALUE_LONG.ref +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %c.patt: %pattern_type.4a9 = value_binding_pattern c [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE_UNSIGNED.ref: %u32 = name_ref CONFIG_VALUE_UNSIGNED, imports.%int_42.58b [concrete = constants.%int_42.58b] +// CHECK:STDOUT: %.loc10: type = splice_block %u32.loc10 [concrete = constants.%u32] { +// CHECK:STDOUT: %int_32.loc10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %u32.loc10: type = class_type @UInt, @UInt(constants.%int_32) [concrete = constants.%u32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %c: %u32 = value_binding c, %CONFIG_VALUE_UNSIGNED.ref +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %d.patt: %pattern_type.7ce = value_binding_pattern d [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE_HEXA.ref: %i32 = name_ref CONFIG_VALUE_HEXA, imports.%int_255 [concrete = constants.%int_255] +// CHECK:STDOUT: %.loc11: type = splice_block %i32.loc11 [concrete = constants.%i32] { +// CHECK:STDOUT: %int_32.loc11: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc11: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %d: %i32 = value_binding d, %CONFIG_VALUE_HEXA.ref +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %e.patt: %pattern_type.7ce = value_binding_pattern e [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc12: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE_OCTAL.ref: %i32 = name_ref CONFIG_VALUE_OCTAL, imports.%int_8 [concrete = constants.%int_8] +// CHECK:STDOUT: %.loc12: type = splice_block %i32.loc12 [concrete = constants.%i32] { +// CHECK:STDOUT: %int_32.loc12: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc12: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %e: %i32 = value_binding e, %CONFIG_VALUE_OCTAL.ref +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %f.patt: %pattern_type.7ce = value_binding_pattern f [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.ref.loc13: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %CONFIG_VALUE_BINARY.ref: %i32 = name_ref CONFIG_VALUE_BINARY, imports.%int_5 [concrete = constants.%int_5] +// CHECK:STDOUT: %.loc13: type = splice_block %i32.loc13 [concrete = constants.%i32] { +// CHECK:STDOUT: %int_32.loc13: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc13: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %f: %i32 = value_binding f, %CONFIG_VALUE_BINARY.ref +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 297a96a9cc3a..95ee7696f1ad 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -375,6 +375,8 @@ 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) CARBON_DIAGNOSTIC_KIND(IncompleteOperandTypeInCppOperatorLookup)