constant.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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/constant.h"
  5. #include "toolchain/check/cpp/import.h"
  6. #include "toolchain/check/eval.h"
  7. #include "toolchain/diagnostics/format_providers.h"
  8. namespace Carbon::Check {
  9. auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id,
  10. const clang::APValue& ap_value, clang::QualType type)
  11. -> SemIR::ConstantId {
  12. SemIR::TypeId type_id = ImportCppType(context, loc_id, type).type_id;
  13. if (!type_id.has_value()) {
  14. return SemIR::ConstantId::NotConstant;
  15. }
  16. if (ap_value.isInt()) {
  17. if (type->isBooleanType()) {
  18. auto value = SemIR::BoolValue::From(!ap_value.getInt().isZero());
  19. return TryEvalInst(
  20. context, SemIR::BoolLiteral{.type_id = type_id, .value = value});
  21. } else {
  22. CARBON_CHECK(type->isIntegralOrEnumerationType());
  23. IntId int_id = context.ints().Add(ap_value.getInt());
  24. return TryEvalInst(context,
  25. SemIR::IntValue{.type_id = type_id, .int_id = int_id});
  26. }
  27. } else if (ap_value.isFloat()) {
  28. FloatId float_id = context.floats().Add(ap_value.getFloat());
  29. return TryEvalInst(
  30. context, SemIR::FloatValue{.type_id = type_id, .float_id = float_id});
  31. } else {
  32. // TODO: support other types.
  33. return SemIR::ConstantId::NotConstant;
  34. }
  35. }
  36. static auto ConvertConstantToAPValue(Context& context,
  37. SemIR::InstId const_inst_id,
  38. clang::QualType param_type)
  39. -> std::optional<clang::APValue> {
  40. if (param_type->isIntegerType()) {
  41. if (auto int_value =
  42. context.insts().TryGetAs<SemIR::IntValue>(const_inst_id)) {
  43. const auto& ap_int = context.ints().GetAtWidth(
  44. int_value->int_id, context.ast_context().getIntWidth(param_type));
  45. auto aps_int =
  46. llvm::APSInt(ap_int, !param_type->isSignedIntegerOrEnumerationType())
  47. .extOrTrunc(context.ast_context().getIntWidth(param_type));
  48. return clang::APValue(aps_int);
  49. }
  50. }
  51. // TODO: support additional parameter types.
  52. return std::nullopt;
  53. }
  54. static auto ConvertArgToExpr(Context& context, SemIR::InstId arg_inst_id,
  55. clang::QualType param_type) -> clang::Expr* {
  56. auto const_inst_id = context.constant_values().GetConstantInstId(arg_inst_id);
  57. if (!const_inst_id.has_value()) {
  58. return nullptr;
  59. }
  60. auto ap_value = ConvertConstantToAPValue(context, const_inst_id, param_type);
  61. if (!ap_value.has_value()) {
  62. return nullptr;
  63. }
  64. auto* opaque_value_expr = new (context.ast_context()) clang::OpaqueValueExpr(
  65. clang::SourceLocation(), param_type, clang::VK_PRValue);
  66. return clang::ConstantExpr::Create(context.ast_context(), opaque_value_expr,
  67. *ap_value);
  68. }
  69. auto EvalCppCall(Context& context, SemIR::LocId loc_id,
  70. SemIR::ClangDeclId clang_decl_id, SemIR::InstBlockId args_id)
  71. -> SemIR::ConstantId {
  72. const auto& args = context.inst_blocks().Get(args_id);
  73. auto* decl = context.clang_decls().Get(clang_decl_id).GetAsKey().decl;
  74. auto* function_decl = cast<clang::FunctionDecl>(decl);
  75. // Create expr for the function declaration.
  76. auto* decl_ref_expr = clang::DeclRefExpr::Create(
  77. context.ast_context(), /*QualifierLoc=*/clang::NestedNameSpecifierLoc(),
  78. /*TemplateKWLoc=*/clang::SourceLocation(), function_decl,
  79. /*RefersToEnclosingVariableOrCapture=*/false,
  80. /*NameLoc=*/clang::SourceLocation(), function_decl->getType(),
  81. clang::VK_LValue);
  82. // Cast to a function pointer type.
  83. auto function_ptr_type =
  84. context.ast_context().getPointerType(function_decl->getType());
  85. auto* implicit_cast_expr = clang::ImplicitCastExpr::Create(
  86. context.ast_context(), function_ptr_type,
  87. clang::CK_FunctionToPointerDecay, decl_ref_expr, nullptr,
  88. clang::VK_PRValue, clang::FPOptionsOverride());
  89. // Convert the arguments to exprs.
  90. clang::SmallVector<clang::Expr*> arg_exprs;
  91. for (auto [arg_inst_id, parm_var_decl] :
  92. llvm::zip(args, function_decl->parameters())) {
  93. if (auto* arg_expr =
  94. ConvertArgToExpr(context, arg_inst_id, parm_var_decl->getType())) {
  95. arg_exprs.push_back(arg_expr);
  96. } else {
  97. return SemIR::ConstantId::NotConstant;
  98. }
  99. }
  100. // Create an expr to call the function.
  101. auto* call_expr = clang::CallExpr::Create(
  102. context.ast_context(), implicit_cast_expr, arg_exprs,
  103. function_decl->getCallResultType(), clang::VK_PRValue,
  104. /*RParenLoc=*/clang::SourceLocation(), clang::FPOptionsOverride());
  105. // Evaluate the expr as a constant and map that to Carbon constant.
  106. clang::Expr::EvalResult eval_result;
  107. if (!call_expr->EvaluateAsConstantExpr(eval_result, context.ast_context())) {
  108. // TODO: improve this diagnostic with information from `eval_result`.
  109. CARBON_DIAGNOSTIC(CppConstexprEval, Error,
  110. "failed to evaluate {0:consteval|constexpr} function "
  111. "call as a constant",
  112. Diagnostics::BoolAsSelect);
  113. context.emitter().Emit(loc_id, CppConstexprEval,
  114. function_decl->isConsteval());
  115. return SemIR::ErrorInst::ConstantId;
  116. }
  117. return MapAPValueToConstant(context, loc_id, eval_result.Val,
  118. function_decl->getCallResultType());
  119. }
  120. } // namespace Carbon::Check