macros.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/cpp/macros.h"
  5. #include "clang/AST/ASTContext.h"
  6. #include "clang/AST/Expr.h"
  7. #include "clang/Parse/Parser.h"
  8. #include "clang/Sema/Sema.h"
  9. #include "common/check.h"
  10. #include "toolchain/check/cpp/constant.h"
  11. #include "toolchain/check/cpp/import.h"
  12. #include "toolchain/check/literal.h"
  13. namespace Carbon::Check {
  14. // Maps a Clang literal expression to a Carbon constant.
  15. static auto MapConstant(Context& context, SemIR::LocId loc_id,
  16. clang::Expr* expr) -> SemIR::InstId {
  17. CARBON_CHECK(expr, "empty expression");
  18. if (auto* string_literal = dyn_cast<clang::StringLiteral>(expr)) {
  19. if (!string_literal->isOrdinary() && !string_literal->isUTF8()) {
  20. context.TODO(loc_id,
  21. llvm::formatv("Unsupported: string literal type: {0}",
  22. expr->getType()));
  23. return SemIR::ErrorInst::InstId;
  24. }
  25. StringLiteralValueId string_id =
  26. context.string_literal_values().Add(string_literal->getString());
  27. auto inst_id =
  28. MakeStringLiteral(context, Parse::StringLiteralId::None, string_id);
  29. return inst_id;
  30. } else if (isa<clang::CXXNullPtrLiteralExpr>(expr)) {
  31. auto type_id = ImportCppType(context, loc_id, expr->getType()).type_id;
  32. return GetOrAddInst<SemIR::UninitializedValue>(context, SemIR::LocId::None,
  33. {.type_id = type_id});
  34. }
  35. context.TODO(loc_id,
  36. llvm::formatv("Unsupported: C++ constant expression type: '{0}'",
  37. expr->getType().getAsString()));
  38. return SemIR::ErrorInst::InstId;
  39. }
  40. auto TryEvaluateMacro(Context& context, SemIR::LocId loc_id,
  41. SemIR::NameId name_id, clang::MacroInfo* macro_info)
  42. -> SemIR::InstId {
  43. auto name_str_opt = context.names().GetAsStringIfIdentifier(name_id);
  44. CARBON_CHECK(macro_info, "macro info missing");
  45. if (macro_info->getNumTokens() == 0) {
  46. context.TODO(loc_id, "Unsupported: macro with 0 replacement tokens");
  47. return SemIR::ErrorInst::InstId;
  48. }
  49. clang::Sema& sema = context.clang_sema();
  50. clang::Preprocessor& preprocessor = sema.getPreprocessor();
  51. auto& parser = context.cpp_context()->parser();
  52. llvm::SmallVector<clang::Token> tokens(macro_info->tokens().begin(),
  53. macro_info->tokens().end());
  54. clang::Token current_token = parser.getCurToken();
  55. // Add eof token
  56. clang::Token eof;
  57. eof.startToken();
  58. eof.setKind(clang::tok::eof);
  59. eof.setLocation(current_token.getEndLoc());
  60. tokens.push_back(eof);
  61. tokens.push_back(current_token);
  62. preprocessor.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false,
  63. /*IsReinject=*/false);
  64. parser.ConsumeAnyToken(true);
  65. clang::ExprResult result = parser.ParseConstantExpression();
  66. clang::Expr* result_expr = result.get();
  67. bool success =
  68. !result.isInvalid() && parser.getCurToken().is(clang::tok::eof);
  69. if (!success) {
  70. parser.SkipUntil(clang::tok::eof);
  71. CARBON_DIAGNOSTIC(
  72. InCppMacroEvaluation, Error,
  73. "failed to parse macro Cpp.{0} to a valid constant expression",
  74. std::string);
  75. context.emitter().Emit(loc_id, InCppMacroEvaluation, (*name_str_opt).str());
  76. return SemIR::ErrorInst::InstId;
  77. }
  78. result_expr = result_expr->IgnoreParenImpCasts();
  79. if (isa<clang::StringLiteral>(result_expr) ||
  80. isa<clang::CXXNullPtrLiteralExpr>(result_expr)) {
  81. return MapConstant(context, loc_id, result_expr);
  82. }
  83. clang::Expr::EvalResult evaluated_result;
  84. if (!result_expr->EvaluateAsConstantExpr(evaluated_result,
  85. sema.getASTContext())) {
  86. CARBON_FATAL("failed to evaluate macro as constant expression");
  87. }
  88. clang::APValue ap_value = evaluated_result.Val;
  89. // TODO: Add support for other types.
  90. if (result_expr->isGLValue()) {
  91. const auto* value_decl =
  92. ap_value.getLValueBase().get<const clang::ValueDecl*>();
  93. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
  94. // TODO: can this const_cast be avoided?
  95. const_cast<clang::ValueDecl*>(value_decl));
  96. if (ap_value.hasLValuePath() && ap_value.getLValuePath().size() > 0) {
  97. context.TODO(loc_id, "Macro evaluated to an lvalue with a path: " +
  98. ap_value.getAsString(context.ast_context(),
  99. result_expr->getType()));
  100. return SemIR::ErrorInst::InstId;
  101. }
  102. return ImportCppDecl(context, loc_id, key);
  103. } else {
  104. auto const_id =
  105. MapAPValueToConstant(context, loc_id, ap_value, result_expr->getType());
  106. if (const_id == SemIR::ConstantId::NotConstant) {
  107. context.TODO(loc_id,
  108. "Unsupported: macro evaluated to a constant of type: " +
  109. result_expr->getType().getAsString());
  110. return SemIR::ErrorInst::InstId;
  111. }
  112. return context.constant_values().GetInstId(const_id);
  113. }
  114. }
  115. } // namespace Carbon::Check