blob: cbe265971df689fc4ef5d68904fb3907574a5cf1 [file] [log] [blame]
Saar Raz5d98ba62019-10-15 15:24:26 +00001//===-- 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"
Saar Razffa214e2019-10-25 00:09:37 +030015#include "clang/Sema/SemaInternal.h"
Saar Raz5d98ba62019-10-15 15:24:26 +000016#include "clang/Sema/SemaDiagnostic.h"
17#include "clang/Sema/TemplateDeduction.h"
18#include "clang/Sema/Template.h"
19#include "clang/AST/ExprCXX.h"
Saar Razffa214e2019-10-25 00:09:37 +030020#include "llvm/ADT/DenseMap.h"
21#include "llvm/ADT/PointerUnion.h"
Saar Raz5d98ba62019-10-15 15:24:26 +000022using namespace clang;
23using namespace sema;
24
25bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
26 // C++2a [temp.constr.atomic]p1
27 // ..E shall be a constant expression of type bool.
28
29 ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts();
30
31 if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
32 if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
33 return CheckConstraintExpression(BinOp->getLHS()) &&
34 CheckConstraintExpression(BinOp->getRHS());
35 } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
36 return CheckConstraintExpression(C->getSubExpr());
37
38 // An atomic constraint!
39 if (ConstraintExpression->isTypeDependent())
40 return true;
41
42 QualType Type = ConstraintExpression->getType();
43 if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
44 Diag(ConstraintExpression->getExprLoc(),
45 diag::err_non_bool_atomic_constraint) << Type
46 << ConstraintExpression->getSourceRange();
47 return false;
48 }
49 return true;
50}
51
Saar Razffa214e2019-10-25 00:09:37 +030052template <typename AtomicEvaluator>
53static bool
54calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
55 ConstraintSatisfaction &Satisfaction,
56 AtomicEvaluator &&Evaluator) {
Saar Raz5d98ba62019-10-15 15:24:26 +000057 ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
58
59 if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
Saar Razffa214e2019-10-25 00:09:37 +030060 if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
61 if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction,
62 Evaluator))
Saar Raz5d98ba62019-10-15 15:24:26 +000063 return true;
Saar Razffa214e2019-10-25 00:09:37 +030064
65 bool IsLHSSatisfied = Satisfaction.IsSatisfied;
66
67 if (BO->getOpcode() == BO_LOr && IsLHSSatisfied)
68 // [temp.constr.op] p3
69 // A disjunction is a constraint taking two operands. To determine if
70 // a disjunction is satisfied, the satisfaction of the first operand
71 // is checked. If that is satisfied, the disjunction is satisfied.
72 // Otherwise, the disjunction is satisfied if and only if the second
73 // operand is satisfied.
Saar Raz5d98ba62019-10-15 15:24:26 +000074 return false;
Saar Razffa214e2019-10-25 00:09:37 +030075
76 if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied)
77 // [temp.constr.op] p2
78 // A conjunction is a constraint taking two operands. To determine if
79 // a conjunction is satisfied, the satisfaction of the first operand
80 // is checked. If that is not satisfied, the conjunction is not
81 // satisfied. Otherwise, the conjunction is satisfied if and only if
82 // the second operand is satisfied.
Saar Raz5d98ba62019-10-15 15:24:26 +000083 return false;
Saar Razffa214e2019-10-25 00:09:37 +030084
85 return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction,
86 std::forward<AtomicEvaluator>(Evaluator));
Saar Raz5d98ba62019-10-15 15:24:26 +000087 }
88 }
89 else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
Saar Razffa214e2019-10-25 00:09:37 +030090 return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
91 std::forward<AtomicEvaluator>(Evaluator));
Saar Raz5d98ba62019-10-15 15:24:26 +000092
Saar Razffa214e2019-10-25 00:09:37 +030093 // An atomic constraint expression
94 ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
Saar Raz5d98ba62019-10-15 15:24:26 +000095
Saar Razffa214e2019-10-25 00:09:37 +030096 if (SubstitutedAtomicExpr.isInvalid())
Saar Raz5d98ba62019-10-15 15:24:26 +000097 return true;
98
Saar Razffa214e2019-10-25 00:09:37 +030099 if (!SubstitutedAtomicExpr.isUsable())
100 // Evaluator has decided satisfaction without yielding an expression.
101 return false;
102
103 EnterExpressionEvaluationContext ConstantEvaluated(
104 S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
Saar Raz5d98ba62019-10-15 15:24:26 +0000105 SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
106 Expr::EvalResult EvalResult;
107 EvalResult.Diag = &EvaluationDiags;
Saar Razffa214e2019-10-25 00:09:37 +0300108 if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) {
109 // C++2a [temp.constr.atomic]p1
110 // ...E shall be a constant expression of type bool.
111 S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(),
112 diag::err_non_constant_constraint_expression)
113 << SubstitutedAtomicExpr.get()->getSourceRange();
Saar Raz5d98ba62019-10-15 15:24:26 +0000114 for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
Saar Razffa214e2019-10-25 00:09:37 +0300115 S.Diag(PDiag.first, PDiag.second);
Saar Raz5d98ba62019-10-15 15:24:26 +0000116 return true;
117 }
118
Saar Razffa214e2019-10-25 00:09:37 +0300119 Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue();
120 if (!Satisfaction.IsSatisfied)
121 Satisfaction.Details.emplace_back(ConstraintExpr,
122 SubstitutedAtomicExpr.get());
Saar Raz5d98ba62019-10-15 15:24:26 +0000123
124 return false;
Saar Razffa214e2019-10-25 00:09:37 +0300125}
126
127template <typename TemplateDeclT>
128static bool calculateConstraintSatisfaction(
129 Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,
130 SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
131 const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
132 return calculateConstraintSatisfaction(
133 S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
134 EnterExpressionEvaluationContext ConstantEvaluated(
135 S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
136
137 // Atomic constraint - substitute arguments and check satisfaction.
138 ExprResult SubstitutedExpression;
139 {
140 TemplateDeductionInfo Info(TemplateNameLoc);
141 Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
142 Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,
143 Info, AtomicExpr->getSourceRange());
144 if (Inst.isInvalid())
145 return ExprError();
146 // We do not want error diagnostics escaping here.
147 Sema::SFINAETrap Trap(S);
148 SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
149 MLTAL);
150 if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
151 // C++2a [temp.constr.atomic]p1
152 // ...If substitution results in an invalid type or expression, the
153 // constraint is not satisfied.
154 if (!Trap.hasErrorOccurred())
155 // A non-SFINAE error has occured as a result of this
156 // substitution.
157 return ExprError();
158
159 PartialDiagnosticAt SubstDiag{SourceLocation(),
160 PartialDiagnostic::NullDiagnostic()};
161 Info.takeSFINAEDiagnostic(SubstDiag);
162 // FIXME: Concepts: This is an unfortunate consequence of there
163 // being no serialization code for PartialDiagnostics and the fact
164 // that serializing them would likely take a lot more storage than
165 // just storing them as strings. We would still like, in the
166 // future, to serialize the proper PartialDiagnostic as serializing
167 // it as a string defeats the purpose of the diagnostic mechanism.
168 SmallString<128> DiagString;
169 DiagString = ": ";
170 SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
171 Satisfaction.Details.emplace_back(
172 AtomicExpr,
173 new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
174 SubstDiag.first,
175 std::string(DiagString.begin(), DiagString.end())});
176 Satisfaction.IsSatisfied = false;
177 return ExprEmpty();
178 }
179 }
180
181 if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
182 return ExprError();
183
184 return SubstitutedExpression;
185 });
186}
187
188template<typename TemplateDeclT>
189static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
190 ArrayRef<const Expr *> ConstraintExprs,
191 ArrayRef<TemplateArgument> TemplateArgs,
192 SourceRange TemplateIDRange,
193 ConstraintSatisfaction &Satisfaction) {
194 if (ConstraintExprs.empty()) {
195 Satisfaction.IsSatisfied = true;
196 return false;
197 }
198
199 for (auto& Arg : TemplateArgs)
200 if (Arg.isInstantiationDependent()) {
201 // No need to check satisfaction for dependent constraint expressions.
202 Satisfaction.IsSatisfied = true;
203 return false;
204 }
205
206 Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
207 Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
208 TemplateIDRange);
209 if (Inst.isInvalid())
210 return true;
211
212 MultiLevelTemplateArgumentList MLTAL;
213 MLTAL.addOuterTemplateArguments(TemplateArgs);
214
215 for (const Expr *ConstraintExpr : ConstraintExprs) {
216 if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
217 TemplateIDRange.getBegin(), MLTAL,
218 ConstraintExpr, Satisfaction))
219 return true;
220 if (!Satisfaction.IsSatisfied)
221 // [temp.constr.op] p2
222 // [...] To determine if a conjunction is satisfied, the satisfaction
223 // of the first operand is checked. If that is not satisfied, the
224 // conjunction is not satisfied. [...]
225 return false;
226 }
227 return false;
228}
229
230bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
231 ArrayRef<const Expr *> ConstraintExprs,
232 ArrayRef<TemplateArgument> TemplateArgs,
233 SourceRange TemplateIDRange,
234 ConstraintSatisfaction &Satisfaction) {
235 return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
236 TemplateArgs, TemplateIDRange,
237 Satisfaction);
238}
239
240bool
241Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
242 ArrayRef<const Expr *> ConstraintExprs,
243 ArrayRef<TemplateArgument> TemplateArgs,
244 SourceRange TemplateIDRange,
245 ConstraintSatisfaction &Satisfaction) {
246 return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs,
247 TemplateArgs, TemplateIDRange,
248 Satisfaction);
249}
250
251bool
252Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
253 ArrayRef<const Expr *> ConstraintExprs,
254 ArrayRef<TemplateArgument> TemplateArgs,
255 SourceRange TemplateIDRange,
256 ConstraintSatisfaction &Satisfaction) {
257 return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs,
258 TemplateArgs, TemplateIDRange,
259 Satisfaction);
260}
261
262bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
263 ConstraintSatisfaction &Satisfaction) {
264 return calculateConstraintSatisfaction(
265 *this, ConstraintExpr, Satisfaction,
266 [](const Expr *AtomicExpr) -> ExprResult {
267 return ExprResult(const_cast<Expr *>(AtomicExpr));
268 });
269}
270
271bool Sema::EnsureTemplateArgumentListConstraints(
272 TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
273 SourceRange TemplateIDRange) {
274 ConstraintSatisfaction Satisfaction;
275 llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
276 TD->getAssociatedConstraints(AssociatedConstraints);
277 if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs,
278 TemplateIDRange, Satisfaction))
279 return true;
280
281 if (!Satisfaction.IsSatisfied) {
282 SmallString<128> TemplateArgString;
283 TemplateArgString = " ";
284 TemplateArgString += getTemplateArgumentBindingsText(
285 TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
286
287 Diag(TemplateIDRange.getBegin(),
288 diag::err_template_arg_list_constraints_not_satisfied)
289 << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
290 << TemplateArgString << TemplateIDRange;
291 DiagnoseUnsatisfiedConstraint(Satisfaction);
292 return true;
293 }
294 return false;
295}
296
297static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
298 Expr *SubstExpr,
299 bool First = true) {
300 SubstExpr = SubstExpr->IgnoreParenImpCasts();
301 if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
302 switch (BO->getOpcode()) {
303 // These two cases will in practice only be reached when using fold
304 // expressions with || and &&, since otherwise the || and && will have been
305 // broken down into atomic constraints during satisfaction checking.
306 case BO_LOr:
307 // Or evaluated to false - meaning both RHS and LHS evaluated to false.
308 diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
309 diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
310 /*First=*/false);
311 return;
312 case BO_LAnd:
313 bool LHSSatisfied;
314 BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context);
315 if (LHSSatisfied) {
316 // LHS is true, so RHS must be false.
317 diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First);
318 return;
319 }
320 // LHS is false
321 diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
322
323 // RHS might also be false
324 bool RHSSatisfied;
325 BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context);
326 if (!RHSSatisfied)
327 diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
328 /*First=*/false);
329 return;
330 case BO_GE:
331 case BO_LE:
332 case BO_GT:
333 case BO_LT:
334 case BO_EQ:
335 case BO_NE:
336 if (BO->getLHS()->getType()->isIntegerType() &&
337 BO->getRHS()->getType()->isIntegerType()) {
338 Expr::EvalResult SimplifiedLHS;
339 Expr::EvalResult SimplifiedRHS;
340 BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
341 BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
342 if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
343 S.Diag(SubstExpr->getBeginLoc(),
344 diag::note_atomic_constraint_evaluated_to_false_elaborated)
345 << (int)First << SubstExpr
346 << SimplifiedLHS.Val.getInt().toString(10)
347 << BinaryOperator::getOpcodeStr(BO->getOpcode())
348 << SimplifiedRHS.Val.getInt().toString(10);
349 return;
350 }
351 }
352 break;
353
354 default:
355 break;
356 }
357 } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
358 if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
359 S.Diag(
360 CSE->getSourceRange().getBegin(),
361 diag::
362 note_single_arg_concept_specialization_constraint_evaluated_to_false)
363 << (int)First
364 << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
365 << CSE->getNamedConcept();
366 } else {
367 S.Diag(SubstExpr->getSourceRange().getBegin(),
368 diag::note_concept_specialization_constraint_evaluated_to_false)
369 << (int)First << CSE;
370 }
371 S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
372 return;
373 }
374
375 S.Diag(SubstExpr->getSourceRange().getBegin(),
376 diag::note_atomic_constraint_evaluated_to_false)
377 << (int)First << SubstExpr;
378}
379
380template<typename SubstitutionDiagnostic>
381static void diagnoseUnsatisfiedConstraintExpr(
382 Sema &S, const Expr *E,
383 const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,
384 bool First = true) {
385 if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()){
386 S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
387 << Diag->second;
388 return;
389 }
390
391 diagnoseWellFormedUnsatisfiedConstraintExpr(S,
392 Record.template get<Expr *>(), First);
393}
394
395void Sema::DiagnoseUnsatisfiedConstraint(
396 const ConstraintSatisfaction& Satisfaction) {
397 assert(!Satisfaction.IsSatisfied &&
398 "Attempted to diagnose a satisfied constraint");
399 bool First = true;
400 for (auto &Pair : Satisfaction.Details) {
401 diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
402 First = false;
403 }
404}
405
406void Sema::DiagnoseUnsatisfiedConstraint(
407 const ASTConstraintSatisfaction &Satisfaction) {
408 assert(!Satisfaction.IsSatisfied &&
409 "Attempted to diagnose a satisfied constraint");
410 bool First = true;
411 for (auto &Pair : Satisfaction) {
412 diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
413 First = false;
414 }
Saar Raz0330fba2019-10-15 18:44:06 +0000415}