xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp (revision a7dea1671b87c07d2d266f836bfa8b58efc7c134)
1 //===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file implements semantic analysis for C++ constraints and concepts.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Sema/Sema.h"
15 #include "clang/Sema/SemaDiagnostic.h"
16 #include "clang/Sema/TemplateDeduction.h"
17 #include "clang/Sema/Template.h"
18 #include "clang/AST/ExprCXX.h"
19 using namespace clang;
20 using namespace sema;
21 
22 bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
23   // C++2a [temp.constr.atomic]p1
24   // ..E shall be a constant expression of type bool.
25 
26   ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts();
27 
28   if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
29     if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
30       return CheckConstraintExpression(BinOp->getLHS()) &&
31              CheckConstraintExpression(BinOp->getRHS());
32   } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
33     return CheckConstraintExpression(C->getSubExpr());
34 
35   // An atomic constraint!
36   if (ConstraintExpression->isTypeDependent())
37     return true;
38 
39   QualType Type = ConstraintExpression->getType();
40   if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
41     Diag(ConstraintExpression->getExprLoc(),
42          diag::err_non_bool_atomic_constraint) << Type
43         << ConstraintExpression->getSourceRange();
44     return false;
45   }
46   return true;
47 }
48 
49 bool
50 Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
51                                       MultiLevelTemplateArgumentList &MLTAL,
52                                       Expr *ConstraintExpr,
53                                       bool &IsSatisfied) {
54   ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
55 
56   if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
57     if (BO->getOpcode() == BO_LAnd) {
58       if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
59                                           IsSatisfied))
60         return true;
61       if (!IsSatisfied)
62         return false;
63       return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
64                                              IsSatisfied);
65     } else if (BO->getOpcode() == BO_LOr) {
66       if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
67                                           IsSatisfied))
68         return true;
69       if (IsSatisfied)
70         return false;
71       return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
72                                              IsSatisfied);
73     }
74   }
75   else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
76     return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
77                                            IsSatisfied);
78 
79   EnterExpressionEvaluationContext ConstantEvaluated(
80       *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
81 
82   // Atomic constraint - substitute arguments and check satisfaction.
83   ExprResult E;
84   {
85     TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());
86     InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),
87                                InstantiatingTemplate::ConstraintSubstitution{},
88                                NamedConcept, Info,
89                                ConstraintExpr->getSourceRange());
90     if (Inst.isInvalid())
91       return true;
92     // We do not want error diagnostics escaping here.
93     Sema::SFINAETrap Trap(*this);
94 
95     E = SubstExpr(ConstraintExpr, MLTAL);
96     if (E.isInvalid() || Trap.hasErrorOccurred()) {
97       // C++2a [temp.constr.atomic]p1
98       //   ...If substitution results in an invalid type or expression, the
99       //   constraint is not satisfied.
100       IsSatisfied = false;
101       return false;
102     }
103   }
104 
105   if (!CheckConstraintExpression(E.get()))
106     return true;
107 
108   SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
109   Expr::EvalResult EvalResult;
110   EvalResult.Diag = &EvaluationDiags;
111   if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {
112     // C++2a [temp.constr.atomic]p1
113     //   ...E shall be a constant expression of type bool.
114     Diag(E.get()->getBeginLoc(),
115          diag::err_non_constant_constraint_expression)
116         << E.get()->getSourceRange();
117     for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
118       Diag(PDiag.first, PDiag.second);
119     return true;
120   }
121 
122   IsSatisfied = EvalResult.Val.getInt().getBoolValue();
123 
124   return false;
125 }