ExprConcepts.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. /// \file
  15. /// Defines Expressions and AST nodes for C++2a concepts.
  16. //
  17. //===----------------------------------------------------------------------===//
  18. #ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H
  19. #define LLVM_CLANG_AST_EXPRCONCEPTS_H
  20. #include "clang/AST/ASTContext.h"
  21. #include "clang/AST/ASTConcept.h"
  22. #include "clang/AST/Decl.h"
  23. #include "clang/AST/DeclarationName.h"
  24. #include "clang/AST/DeclTemplate.h"
  25. #include "clang/AST/Expr.h"
  26. #include "clang/AST/NestedNameSpecifier.h"
  27. #include "clang/AST/TemplateBase.h"
  28. #include "clang/AST/Type.h"
  29. #include "clang/Basic/SourceLocation.h"
  30. #include "llvm/Support/ErrorHandling.h"
  31. #include "llvm/Support/TrailingObjects.h"
  32. #include <utility>
  33. #include <string>
  34. namespace clang {
  35. class ASTStmtReader;
  36. class ASTStmtWriter;
  37. /// \brief Represents the specialization of a concept - evaluates to a prvalue
  38. /// of type bool.
  39. ///
  40. /// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
  41. /// specialization of a concept results in a prvalue of type bool.
  42. class ConceptSpecializationExpr final : public Expr, public ConceptReference {
  43. friend class ASTReader;
  44. friend class ASTStmtReader;
  45. public:
  46. using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
  47. protected:
  48. /// \brief The Implicit Concept Specialization Decl, which holds the template
  49. /// arguments for this specialization.
  50. ImplicitConceptSpecializationDecl *SpecDecl;
  51. /// \brief Information about the satisfaction of the named concept with the
  52. /// given arguments. If this expression is value dependent, this is to be
  53. /// ignored.
  54. ASTConstraintSatisfaction *Satisfaction;
  55. ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
  56. SourceLocation TemplateKWLoc,
  57. DeclarationNameInfo ConceptNameInfo,
  58. NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
  59. const ASTTemplateArgumentListInfo *ArgsAsWritten,
  60. ImplicitConceptSpecializationDecl *SpecDecl,
  61. const ConstraintSatisfaction *Satisfaction);
  62. ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept,
  63. ImplicitConceptSpecializationDecl *SpecDecl,
  64. const ConstraintSatisfaction *Satisfaction,
  65. bool Dependent,
  66. bool ContainsUnexpandedParameterPack);
  67. ConceptSpecializationExpr(EmptyShell Empty);
  68. public:
  69. static ConceptSpecializationExpr *
  70. Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
  71. SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
  72. NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
  73. const ASTTemplateArgumentListInfo *ArgsAsWritten,
  74. ImplicitConceptSpecializationDecl *SpecDecl,
  75. const ConstraintSatisfaction *Satisfaction);
  76. static ConceptSpecializationExpr *
  77. Create(const ASTContext &C, ConceptDecl *NamedConcept,
  78. ImplicitConceptSpecializationDecl *SpecDecl,
  79. const ConstraintSatisfaction *Satisfaction, bool Dependent,
  80. bool ContainsUnexpandedParameterPack);
  81. ArrayRef<TemplateArgument> getTemplateArguments() const {
  82. return SpecDecl->getTemplateArguments();
  83. }
  84. const ImplicitConceptSpecializationDecl *getSpecializationDecl() const {
  85. assert(SpecDecl && "Template Argument Decl not initialized");
  86. return SpecDecl;
  87. }
  88. /// \brief Whether or not the concept with the given arguments was satisfied
  89. /// when the expression was created.
  90. /// The expression must not be dependent.
  91. bool isSatisfied() const {
  92. assert(!isValueDependent() &&
  93. "isSatisfied called on a dependent ConceptSpecializationExpr");
  94. return Satisfaction->IsSatisfied;
  95. }
  96. /// \brief Get elaborated satisfaction info about the template arguments'
  97. /// satisfaction of the named concept.
  98. /// The expression must not be dependent.
  99. const ASTConstraintSatisfaction &getSatisfaction() const {
  100. assert(!isValueDependent() &&
  101. "getSatisfaction called on dependent ConceptSpecializationExpr");
  102. return *Satisfaction;
  103. }
  104. static bool classof(const Stmt *T) {
  105. return T->getStmtClass() == ConceptSpecializationExprClass;
  106. }
  107. SourceLocation getBeginLoc() const LLVM_READONLY {
  108. if (auto QualifierLoc = getNestedNameSpecifierLoc())
  109. return QualifierLoc.getBeginLoc();
  110. return ConceptName.getBeginLoc();
  111. }
  112. SourceLocation getEndLoc() const LLVM_READONLY {
  113. // If the ConceptSpecializationExpr is the ImmediatelyDeclaredConstraint
  114. // of a TypeConstraint written syntactically as a constrained-parameter,
  115. // there may not be a template argument list.
  116. return ArgsAsWritten->RAngleLoc.isValid() ? ArgsAsWritten->RAngleLoc
  117. : ConceptName.getEndLoc();
  118. }
  119. // Iterators
  120. child_range children() {
  121. return child_range(child_iterator(), child_iterator());
  122. }
  123. const_child_range children() const {
  124. return const_child_range(const_child_iterator(), const_child_iterator());
  125. }
  126. };
  127. namespace concepts {
  128. /// \brief A static requirement that can be used in a requires-expression to
  129. /// check properties of types and expression.
  130. class Requirement {
  131. public:
  132. // Note - simple and compound requirements are both represented by the same
  133. // class (ExprRequirement).
  134. enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested };
  135. private:
  136. const RequirementKind Kind;
  137. // FIXME: use RequirementDependence to model dependence?
  138. bool Dependent : 1;
  139. bool ContainsUnexpandedParameterPack : 1;
  140. bool Satisfied : 1;
  141. public:
  142. struct SubstitutionDiagnostic {
  143. StringRef SubstitutedEntity;
  144. // FIXME: Store diagnostics semantically and not as prerendered strings.
  145. // Fixing this probably requires serialization of PartialDiagnostic
  146. // objects.
  147. SourceLocation DiagLoc;
  148. StringRef DiagMessage;
  149. };
  150. Requirement(RequirementKind Kind, bool IsDependent,
  151. bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) :
  152. Kind(Kind), Dependent(IsDependent),
  153. ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
  154. Satisfied(IsSatisfied) {}
  155. RequirementKind getKind() const { return Kind; }
  156. bool isSatisfied() const {
  157. assert(!Dependent &&
  158. "isSatisfied can only be called on non-dependent requirements.");
  159. return Satisfied;
  160. }
  161. void setSatisfied(bool IsSatisfied) {
  162. assert(!Dependent &&
  163. "setSatisfied can only be called on non-dependent requirements.");
  164. Satisfied = IsSatisfied;
  165. }
  166. void setDependent(bool IsDependent) { Dependent = IsDependent; }
  167. bool isDependent() const { return Dependent; }
  168. void setContainsUnexpandedParameterPack(bool Contains) {
  169. ContainsUnexpandedParameterPack = Contains;
  170. }
  171. bool containsUnexpandedParameterPack() const {
  172. return ContainsUnexpandedParameterPack;
  173. }
  174. };
  175. /// \brief A requires-expression requirement which queries the existence of a
  176. /// type name or type template specialization ('type' requirements).
  177. class TypeRequirement : public Requirement {
  178. public:
  179. enum SatisfactionStatus {
  180. SS_Dependent,
  181. SS_SubstitutionFailure,
  182. SS_Satisfied
  183. };
  184. private:
  185. llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
  186. SatisfactionStatus Status;
  187. public:
  188. friend ASTStmtReader;
  189. friend ASTStmtWriter;
  190. /// \brief Construct a type requirement from a type. If the given type is not
  191. /// dependent, this indicates that the type exists and the requirement will be
  192. /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be
  193. /// used.
  194. TypeRequirement(TypeSourceInfo *T);
  195. /// \brief Construct a type requirement when the nested name specifier is
  196. /// invalid due to a bad substitution. The requirement is unsatisfied.
  197. TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
  198. Requirement(RK_Type, false, false, false), Value(Diagnostic),
  199. Status(SS_SubstitutionFailure) {}
  200. SatisfactionStatus getSatisfactionStatus() const { return Status; }
  201. void setSatisfactionStatus(SatisfactionStatus Status) {
  202. this->Status = Status;
  203. }
  204. bool isSubstitutionFailure() const {
  205. return Status == SS_SubstitutionFailure;
  206. }
  207. SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
  208. assert(Status == SS_SubstitutionFailure &&
  209. "Attempted to get substitution diagnostic when there has been no "
  210. "substitution failure.");
  211. return Value.get<SubstitutionDiagnostic *>();
  212. }
  213. TypeSourceInfo *getType() const {
  214. assert(!isSubstitutionFailure() &&
  215. "Attempted to get type when there has been a substitution failure.");
  216. return Value.get<TypeSourceInfo *>();
  217. }
  218. static bool classof(const Requirement *R) {
  219. return R->getKind() == RK_Type;
  220. }
  221. };
  222. /// \brief A requires-expression requirement which queries the validity and
  223. /// properties of an expression ('simple' and 'compound' requirements).
  224. class ExprRequirement : public Requirement {
  225. public:
  226. enum SatisfactionStatus {
  227. SS_Dependent,
  228. SS_ExprSubstitutionFailure,
  229. SS_NoexceptNotMet,
  230. SS_TypeRequirementSubstitutionFailure,
  231. SS_ConstraintsNotSatisfied,
  232. SS_Satisfied
  233. };
  234. class ReturnTypeRequirement {
  235. llvm::PointerIntPair<
  236. llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>,
  237. 1, bool>
  238. TypeConstraintInfo;
  239. public:
  240. friend ASTStmtReader;
  241. friend ASTStmtWriter;
  242. /// \brief No return type requirement was specified.
  243. ReturnTypeRequirement() : TypeConstraintInfo(nullptr, false) {}
  244. /// \brief A return type requirement was specified but it was a
  245. /// substitution failure.
  246. ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) :
  247. TypeConstraintInfo(SubstDiag, false) {}
  248. /// \brief A 'type constraint' style return type requirement.
  249. /// \param TPL an invented template parameter list containing a single
  250. /// type parameter with a type-constraint.
  251. // TODO: Can we maybe not save the whole template parameter list and just
  252. // the type constraint? Saving the whole TPL makes it easier to handle in
  253. // serialization but is less elegant.
  254. ReturnTypeRequirement(TemplateParameterList *TPL);
  255. bool isDependent() const {
  256. return TypeConstraintInfo.getInt();
  257. }
  258. bool containsUnexpandedParameterPack() const {
  259. if (!isTypeConstraint())
  260. return false;
  261. return getTypeConstraintTemplateParameterList()
  262. ->containsUnexpandedParameterPack();
  263. }
  264. bool isEmpty() const {
  265. return TypeConstraintInfo.getPointer().isNull();
  266. }
  267. bool isSubstitutionFailure() const {
  268. return !isEmpty() &&
  269. TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
  270. }
  271. bool isTypeConstraint() const {
  272. return !isEmpty() &&
  273. TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
  274. }
  275. SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
  276. assert(isSubstitutionFailure());
  277. return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
  278. }
  279. const TypeConstraint *getTypeConstraint() const;
  280. TemplateParameterList *getTypeConstraintTemplateParameterList() const {
  281. assert(isTypeConstraint());
  282. return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
  283. }
  284. };
  285. private:
  286. llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
  287. SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified.
  288. ReturnTypeRequirement TypeReq;
  289. ConceptSpecializationExpr *SubstitutedConstraintExpr;
  290. SatisfactionStatus Status;
  291. public:
  292. friend ASTStmtReader;
  293. friend ASTStmtWriter;
  294. /// \brief Construct a compound requirement.
  295. /// \param E the expression which is checked by this requirement.
  296. /// \param IsSimple whether this was a simple requirement in source.
  297. /// \param NoexceptLoc the location of the noexcept keyword, if it was
  298. /// specified, otherwise an empty location.
  299. /// \param Req the requirement for the type of the checked expression.
  300. /// \param Status the satisfaction status of this requirement.
  301. ExprRequirement(
  302. Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
  303. ReturnTypeRequirement Req, SatisfactionStatus Status,
  304. ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr);
  305. /// \brief Construct a compound requirement whose expression was a
  306. /// substitution failure. The requirement is not satisfied.
  307. /// \param E the diagnostic emitted while instantiating the original
  308. /// expression.
  309. /// \param IsSimple whether this was a simple requirement in source.
  310. /// \param NoexceptLoc the location of the noexcept keyword, if it was
  311. /// specified, otherwise an empty location.
  312. /// \param Req the requirement for the type of the checked expression (omit
  313. /// if no requirement was specified).
  314. ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple,
  315. SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {});
  316. bool isSimple() const { return getKind() == RK_Simple; }
  317. bool isCompound() const { return getKind() == RK_Compound; }
  318. bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
  319. SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
  320. SatisfactionStatus getSatisfactionStatus() const { return Status; }
  321. bool isExprSubstitutionFailure() const {
  322. return Status == SS_ExprSubstitutionFailure;
  323. }
  324. const ReturnTypeRequirement &getReturnTypeRequirement() const {
  325. return TypeReq;
  326. }
  327. ConceptSpecializationExpr *
  328. getReturnTypeRequirementSubstitutedConstraintExpr() const {
  329. assert(Status >= SS_TypeRequirementSubstitutionFailure);
  330. return SubstitutedConstraintExpr;
  331. }
  332. SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const {
  333. assert(isExprSubstitutionFailure() &&
  334. "Attempted to get expression substitution diagnostic when there has "
  335. "been no expression substitution failure");
  336. return Value.get<SubstitutionDiagnostic *>();
  337. }
  338. Expr *getExpr() const {
  339. assert(!isExprSubstitutionFailure() &&
  340. "ExprRequirement has no expression because there has been a "
  341. "substitution failure.");
  342. return Value.get<Expr *>();
  343. }
  344. static bool classof(const Requirement *R) {
  345. return R->getKind() == RK_Compound || R->getKind() == RK_Simple;
  346. }
  347. };
  348. /// \brief A requires-expression requirement which is satisfied when a general
  349. /// constraint expression is satisfied ('nested' requirements).
  350. class NestedRequirement : public Requirement {
  351. Expr *Constraint = nullptr;
  352. const ASTConstraintSatisfaction *Satisfaction = nullptr;
  353. bool HasInvalidConstraint = false;
  354. StringRef InvalidConstraintEntity;
  355. public:
  356. friend ASTStmtReader;
  357. friend ASTStmtWriter;
  358. NestedRequirement(Expr *Constraint)
  359. : Requirement(RK_Nested, /*IsDependent=*/true,
  360. Constraint->containsUnexpandedParameterPack()),
  361. Constraint(Constraint) {
  362. assert(Constraint->isInstantiationDependent() &&
  363. "Nested requirement with non-dependent constraint must be "
  364. "constructed with a ConstraintSatisfaction object");
  365. }
  366. NestedRequirement(ASTContext &C, Expr *Constraint,
  367. const ConstraintSatisfaction &Satisfaction)
  368. : Requirement(RK_Nested, Constraint->isInstantiationDependent(),
  369. Constraint->containsUnexpandedParameterPack(),
  370. Satisfaction.IsSatisfied),
  371. Constraint(Constraint),
  372. Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
  373. NestedRequirement(StringRef InvalidConstraintEntity,
  374. const ASTConstraintSatisfaction *Satisfaction)
  375. : Requirement(RK_Nested,
  376. /*IsDependent=*/false,
  377. /*ContainsUnexpandedParameterPack*/ false,
  378. Satisfaction->IsSatisfied),
  379. Satisfaction(Satisfaction), HasInvalidConstraint(true),
  380. InvalidConstraintEntity(InvalidConstraintEntity) {}
  381. NestedRequirement(ASTContext &C, StringRef InvalidConstraintEntity,
  382. const ConstraintSatisfaction &Satisfaction)
  383. : NestedRequirement(InvalidConstraintEntity,
  384. ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
  385. bool hasInvalidConstraint() const { return HasInvalidConstraint; }
  386. StringRef getInvalidConstraintEntity() {
  387. assert(hasInvalidConstraint());
  388. return InvalidConstraintEntity;
  389. }
  390. Expr *getConstraintExpr() const {
  391. assert(!hasInvalidConstraint() &&
  392. "getConstraintExpr() may not be called "
  393. "on nested requirements with invalid constraint.");
  394. return Constraint;
  395. }
  396. const ASTConstraintSatisfaction &getConstraintSatisfaction() const {
  397. return *Satisfaction;
  398. }
  399. static bool classof(const Requirement *R) {
  400. return R->getKind() == RK_Nested;
  401. }
  402. };
  403. } // namespace concepts
  404. /// C++2a [expr.prim.req]:
  405. /// A requires-expression provides a concise way to express requirements on
  406. /// template arguments. A requirement is one that can be checked by name
  407. /// lookup (6.4) or by checking properties of types and expressions.
  408. /// [...]
  409. /// A requires-expression is a prvalue of type bool [...]
  410. class RequiresExpr final : public Expr,
  411. llvm::TrailingObjects<RequiresExpr, ParmVarDecl *,
  412. concepts::Requirement *> {
  413. friend TrailingObjects;
  414. friend class ASTStmtReader;
  415. unsigned NumLocalParameters;
  416. unsigned NumRequirements;
  417. RequiresExprBodyDecl *Body;
  418. SourceLocation RBraceLoc;
  419. unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
  420. return NumLocalParameters;
  421. }
  422. unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const {
  423. return NumRequirements;
  424. }
  425. RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
  426. RequiresExprBodyDecl *Body,
  427. ArrayRef<ParmVarDecl *> LocalParameters,
  428. ArrayRef<concepts::Requirement *> Requirements,
  429. SourceLocation RBraceLoc);
  430. RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
  431. unsigned NumRequirements);
  432. public:
  433. static RequiresExpr *
  434. Create(ASTContext &C, SourceLocation RequiresKWLoc,
  435. RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters,
  436. ArrayRef<concepts::Requirement *> Requirements,
  437. SourceLocation RBraceLoc);
  438. static RequiresExpr *
  439. Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
  440. unsigned NumRequirements);
  441. ArrayRef<ParmVarDecl *> getLocalParameters() const {
  442. return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
  443. }
  444. RequiresExprBodyDecl *getBody() const { return Body; }
  445. ArrayRef<concepts::Requirement *> getRequirements() const {
  446. return {getTrailingObjects<concepts::Requirement *>(), NumRequirements};
  447. }
  448. /// \brief Whether or not the requires clause is satisfied.
  449. /// The expression must not be dependent.
  450. bool isSatisfied() const {
  451. assert(!isValueDependent()
  452. && "isSatisfied called on a dependent RequiresExpr");
  453. return RequiresExprBits.IsSatisfied;
  454. }
  455. void setSatisfied(bool IsSatisfied) {
  456. assert(!isValueDependent() &&
  457. "setSatisfied called on a dependent RequiresExpr");
  458. RequiresExprBits.IsSatisfied = IsSatisfied;
  459. }
  460. SourceLocation getRequiresKWLoc() const {
  461. return RequiresExprBits.RequiresKWLoc;
  462. }
  463. SourceLocation getRBraceLoc() const { return RBraceLoc; }
  464. static bool classof(const Stmt *T) {
  465. return T->getStmtClass() == RequiresExprClass;
  466. }
  467. SourceLocation getBeginLoc() const LLVM_READONLY {
  468. return RequiresExprBits.RequiresKWLoc;
  469. }
  470. SourceLocation getEndLoc() const LLVM_READONLY {
  471. return RBraceLoc;
  472. }
  473. // Iterators
  474. child_range children() {
  475. return child_range(child_iterator(), child_iterator());
  476. }
  477. const_child_range children() const {
  478. return const_child_range(const_child_iterator(), const_child_iterator());
  479. }
  480. };
  481. } // namespace clang
  482. #endif // LLVM_CLANG_AST_EXPRCONCEPTS_H
  483. #ifdef __GNUC__
  484. #pragma GCC diagnostic pop
  485. #endif