macros.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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. auto const_id = MapAPValueToConstant(context, loc_id, evaluated_result.Val,
  89. result_expr->getType(),
  90. /*is_lvalue=*/result_expr->isGLValue());
  91. if (const_id == SemIR::ConstantId::NotConstant) {
  92. context.TODO(loc_id,
  93. "Unsupported: macro evaluated to a constant of type: " +
  94. result_expr->getType().getAsString());
  95. return SemIR::ErrorInst::InstId;
  96. }
  97. return context.constant_values().GetInstId(const_id);
  98. }
  99. } // namespace Carbon::Check