constant.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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/cpp/type_mapping.h"
  7. #include "toolchain/check/eval.h"
  8. #include "toolchain/check/member_access.h"
  9. #include "toolchain/check/type.h"
  10. #include "toolchain/check/type_completion.h"
  11. #include "toolchain/diagnostics/format_providers.h"
  12. namespace Carbon::Check {
  13. static auto MapLValueToConstant(Context& context, SemIR::LocId loc_id,
  14. const clang::APValue& ap_value,
  15. clang::QualType type) -> SemIR::ConstantId {
  16. CARBON_CHECK(ap_value.isLValue(), "not an LValue");
  17. const auto* value_decl =
  18. ap_value.getLValueBase().get<const clang::ValueDecl*>();
  19. if (!ap_value.hasLValuePath()) {
  20. context.TODO(loc_id, "lvalue has no path");
  21. return SemIR::ErrorInst::ConstantId;
  22. }
  23. if (ap_value.isLValueOnePastTheEnd()) {
  24. context.TODO(loc_id, "one-past-the-end lvalue");
  25. return SemIR::ErrorInst::ConstantId;
  26. }
  27. auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
  28. // TODO: can this const_cast be avoided?
  29. const_cast<clang::ValueDecl*>(value_decl));
  30. auto inst_id = ImportCppDecl(context, loc_id, key);
  31. if (ap_value.getLValuePath().size() == 0) {
  32. return context.constant_values().Get(inst_id);
  33. }
  34. // Import the base type so that its fields can be accessed.
  35. auto var_storage = context.insts().GetAs<SemIR::VarStorage>(inst_id);
  36. // TODO: currently an error isn't reachable here because incomplete
  37. // array types can't be imported. Once that changes, switch to
  38. // `RequireCompleteType` and handle the error.
  39. CompleteTypeOrCheckFail(context, var_storage.type_id);
  40. clang::QualType qual_type = ap_value.getLValueBase().getType();
  41. for (const auto& entry : ap_value.getLValuePath()) {
  42. if (qual_type->isArrayType()) {
  43. context.TODO(loc_id, "lvalue path contains an array type");
  44. } else {
  45. const auto* decl =
  46. cast<clang::Decl>(entry.getAsBaseOrMember().getPointer());
  47. const auto* field_decl = dyn_cast<clang::FieldDecl>(decl);
  48. if (!field_decl) {
  49. context.TODO(loc_id, "lvalue path contains a base class subobject");
  50. return SemIR::ErrorInst::ConstantId;
  51. }
  52. auto field_inst_id =
  53. ImportCppDecl(context, loc_id,
  54. SemIR::ClangDeclKey::ForNonFunctionDecl(
  55. const_cast<clang::FieldDecl*>(field_decl)));
  56. if (field_inst_id == SemIR::ErrorInst::InstId) {
  57. context.TODO(loc_id,
  58. "unsupported field in lvalue path: " +
  59. ap_value.getAsString(context.ast_context(), type));
  60. return SemIR::ErrorInst::ConstantId;
  61. }
  62. const SemIR::FieldDecl& field_decl_inst =
  63. context.insts().GetAs<SemIR::FieldDecl>(field_inst_id);
  64. qual_type = field_decl->getType();
  65. inst_id = PerformMemberAccess(context, loc_id, inst_id,
  66. field_decl_inst.name_id);
  67. }
  68. }
  69. return context.constant_values().Get(inst_id);
  70. }
  71. auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id,
  72. const clang::APValue& ap_value, clang::QualType type,
  73. bool is_lvalue) -> SemIR::ConstantId {
  74. SemIR::TypeId type_id = ImportCppType(context, loc_id, type).type_id;
  75. if (!type_id.has_value()) {
  76. return SemIR::ConstantId::NotConstant;
  77. }
  78. if (is_lvalue) {
  79. return MapLValueToConstant(context, loc_id, ap_value, type);
  80. } else if (type->isPointerType()) {
  81. auto const_id = MapLValueToConstant(context, loc_id, ap_value, type);
  82. auto inst_id = AddInst<SemIR::AddrOf>(
  83. context, loc_id,
  84. {.type_id = type_id,
  85. .lvalue_id = context.constant_values().GetInstId(const_id)});
  86. return context.constant_values().Get(inst_id);
  87. } else if (ap_value.isInt()) {
  88. if (type->isBooleanType()) {
  89. auto value = SemIR::BoolValue::From(!ap_value.getInt().isZero());
  90. return TryEvalInst(
  91. context, SemIR::BoolLiteral{.type_id = type_id, .value = value});
  92. } else {
  93. CARBON_CHECK(type->isIntegralOrEnumerationType());
  94. IntId int_id = context.ints().Add(ap_value.getInt());
  95. return TryEvalInst(context,
  96. SemIR::IntValue{.type_id = type_id, .int_id = int_id});
  97. }
  98. } else if (ap_value.isFloat()) {
  99. FloatId float_id = context.floats().Add(ap_value.getFloat());
  100. return TryEvalInst(
  101. context, SemIR::FloatValue{.type_id = type_id, .float_id = float_id});
  102. } else {
  103. // TODO: support other types.
  104. context.TODO(loc_id, "unsupported conversion to constant from APValue " +
  105. ap_value.getAsString(context.ast_context(), type));
  106. return SemIR::ErrorInst::ConstantId;
  107. }
  108. }
  109. static auto MapAPValueToConstantForConstexpr(Context& context,
  110. SemIR::LocId loc_id,
  111. const clang::APValue& ap_value,
  112. clang::QualType type)
  113. -> SemIR::ConstantId {
  114. bool is_lvalue = false;
  115. if (type->isReferenceType()) {
  116. is_lvalue = true;
  117. type = type.getNonReferenceType();
  118. }
  119. return MapAPValueToConstant(context, loc_id, ap_value, type, is_lvalue);
  120. }
  121. auto EvalCppVarDecl(Context& context, SemIR::LocId loc_id,
  122. const clang::VarDecl* var_decl, SemIR::TypeId type_id)
  123. -> SemIR::ConstantId {
  124. // If the C++ global is constant, map it to a Carbon constant.
  125. if (var_decl->isUsableInConstantExpressions(context.ast_context())) {
  126. if (const auto* ap_value = var_decl->getEvaluatedValue()) {
  127. auto clang_type = MapToCppType(context, type_id);
  128. if (clang_type.isNull()) {
  129. context.TODO(loc_id, "failed to map C++ type to Carbon");
  130. return SemIR::ErrorInst::ConstantId;
  131. }
  132. return MapAPValueToConstantForConstexpr(context, loc_id, *ap_value,
  133. clang_type);
  134. }
  135. }
  136. return SemIR::ConstantId::NotConstant;
  137. }
  138. static auto ConvertConstantToAPValue(Context& context,
  139. SemIR::InstId const_inst_id,
  140. clang::QualType param_type)
  141. -> std::optional<clang::APValue> {
  142. if (param_type->isIntegerType()) {
  143. if (auto int_value =
  144. context.insts().TryGetAs<SemIR::IntValue>(const_inst_id)) {
  145. const auto& ap_int = context.ints().GetAtWidth(
  146. int_value->int_id, context.ast_context().getIntWidth(param_type));
  147. auto aps_int =
  148. llvm::APSInt(ap_int, !param_type->isSignedIntegerOrEnumerationType())
  149. .extOrTrunc(context.ast_context().getIntWidth(param_type));
  150. return clang::APValue(aps_int);
  151. }
  152. }
  153. // TODO: support additional parameter types.
  154. return std::nullopt;
  155. }
  156. static auto ConvertArgToExpr(Context& context, SemIR::InstId arg_inst_id,
  157. clang::QualType param_type) -> clang::Expr* {
  158. auto const_inst_id = context.constant_values().GetConstantInstId(arg_inst_id);
  159. if (!const_inst_id.has_value()) {
  160. return nullptr;
  161. }
  162. auto ap_value = ConvertConstantToAPValue(context, const_inst_id, param_type);
  163. if (!ap_value.has_value()) {
  164. return nullptr;
  165. }
  166. auto* opaque_value_expr = new (context.ast_context()) clang::OpaqueValueExpr(
  167. clang::SourceLocation(), param_type, clang::VK_PRValue);
  168. return clang::ConstantExpr::Create(context.ast_context(), opaque_value_expr,
  169. *ap_value);
  170. }
  171. auto EvalCppCall(Context& context, SemIR::LocId loc_id,
  172. SemIR::ClangDeclId clang_decl_id, SemIR::InstBlockId args_id)
  173. -> SemIR::ConstantId {
  174. const auto& args = context.inst_blocks().Get(args_id);
  175. auto* decl = context.clang_decls().Get(clang_decl_id).GetAsKey().decl;
  176. auto* function_decl = cast<clang::FunctionDecl>(decl);
  177. // Create expr for the function declaration.
  178. auto* decl_ref_expr = clang::DeclRefExpr::Create(
  179. context.ast_context(), /*QualifierLoc=*/clang::NestedNameSpecifierLoc(),
  180. /*TemplateKWLoc=*/clang::SourceLocation(), function_decl,
  181. /*RefersToEnclosingVariableOrCapture=*/false,
  182. /*NameLoc=*/clang::SourceLocation(), function_decl->getType(),
  183. clang::VK_LValue);
  184. // Cast to a function pointer type.
  185. auto function_ptr_type =
  186. context.ast_context().getPointerType(function_decl->getType());
  187. auto* implicit_cast_expr = clang::ImplicitCastExpr::Create(
  188. context.ast_context(), function_ptr_type,
  189. clang::CK_FunctionToPointerDecay, decl_ref_expr, nullptr,
  190. clang::VK_PRValue, clang::FPOptionsOverride());
  191. // Convert the arguments to exprs.
  192. clang::SmallVector<clang::Expr*> arg_exprs;
  193. for (auto [arg_inst_id, parm_var_decl] :
  194. llvm::zip(args, function_decl->parameters())) {
  195. if (auto* arg_expr =
  196. ConvertArgToExpr(context, arg_inst_id, parm_var_decl->getType())) {
  197. arg_exprs.push_back(arg_expr);
  198. } else {
  199. return SemIR::ConstantId::NotConstant;
  200. }
  201. }
  202. // Create an expr to call the function.
  203. auto* call_expr = clang::CallExpr::Create(
  204. context.ast_context(), implicit_cast_expr, arg_exprs,
  205. function_decl->getCallResultType(), clang::VK_PRValue,
  206. /*RParenLoc=*/clang::SourceLocation(), clang::FPOptionsOverride());
  207. // Evaluate the expr as a constant and map that to Carbon constant.
  208. clang::Expr::EvalResult eval_result;
  209. if (!call_expr->EvaluateAsConstantExpr(eval_result, context.ast_context())) {
  210. // TODO: improve this diagnostic with information from `eval_result`.
  211. CARBON_DIAGNOSTIC(CppConstexprEval, Error,
  212. "failed to evaluate {0:consteval|constexpr} function "
  213. "call as a constant",
  214. Diagnostics::BoolAsSelect);
  215. context.emitter().Emit(loc_id, CppConstexprEval,
  216. function_decl->isConsteval());
  217. return SemIR::ErrorInst::ConstantId;
  218. }
  219. return MapAPValueToConstantForConstexpr(context, loc_id, eval_result.Val,
  220. function_decl->getCallResultType());
  221. }
  222. } // namespace Carbon::Check