summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaConcept.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaConcept.cpp')
-rw-r--r--clang/lib/Sema/SemaConcept.cpp398
1 files changed, 54 insertions, 344 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index cbe265971df..848ccf54344 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -12,13 +12,10 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/Sema.h"
-#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/Template.h"
#include "clang/AST/ExprCXX.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/PointerUnion.h"
using namespace clang;
using namespace sema;
@@ -49,367 +46,80 @@ bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
return true;
}
-template <typename AtomicEvaluator>
-static bool
-calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction,
- AtomicEvaluator &&Evaluator) {
+bool
+Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
+ MultiLevelTemplateArgumentList &MLTAL,
+ Expr *ConstraintExpr,
+ bool &IsSatisfied) {
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
- if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
- if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction,
- Evaluator))
+ if (BO->getOpcode() == BO_LAnd) {
+ if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+ IsSatisfied))
return true;
-
- bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
- if (BO->getOpcode() == BO_LOr && IsLHSSatisfied)
- // [temp.constr.op] p3
- // A disjunction is a constraint taking two operands. To determine if
- // a disjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is satisfied, the disjunction is satisfied.
- // Otherwise, the disjunction is satisfied if and only if the second
- // operand is satisfied.
+ if (!IsSatisfied)
return false;
-
- if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied)
- // [temp.constr.op] p2
- // A conjunction is a constraint taking two operands. To determine if
- // a conjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is not satisfied, the conjunction is not
- // satisfied. Otherwise, the conjunction is satisfied if and only if
- // the second operand is satisfied.
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+ IsSatisfied);
+ } else if (BO->getOpcode() == BO_LOr) {
+ if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+ IsSatisfied))
+ return true;
+ if (IsSatisfied)
return false;
-
- return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction,
- std::forward<AtomicEvaluator>(Evaluator));
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+ IsSatisfied);
}
}
else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
- return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
- std::forward<AtomicEvaluator>(Evaluator));
-
- // An atomic constraint expression
- ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
-
- if (SubstitutedAtomicExpr.isInvalid())
- return true;
-
- if (!SubstitutedAtomicExpr.isUsable())
- // Evaluator has decided satisfaction without yielding an expression.
- return false;
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
+ IsSatisfied);
EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
- Expr::EvalResult EvalResult;
- EvalResult.Diag = &EvaluationDiags;
- if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) {
- // C++2a [temp.constr.atomic]p1
- // ...E shall be a constant expression of type bool.
- S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(),
- diag::err_non_constant_constraint_expression)
- << SubstitutedAtomicExpr.get()->getSourceRange();
- for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
- S.Diag(PDiag.first, PDiag.second);
- return true;
- }
-
- Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue();
- if (!Satisfaction.IsSatisfied)
- Satisfaction.Details.emplace_back(ConstraintExpr,
- SubstitutedAtomicExpr.get());
-
- return false;
-}
-
-template <typename TemplateDeclT>
-static bool calculateConstraintSatisfaction(
- Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs,
- SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
- const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
- return calculateConstraintSatisfaction(
- S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
- EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-
- // Atomic constraint - substitute arguments and check satisfaction.
- ExprResult SubstitutedExpression;
- {
- TemplateDeductionInfo Info(TemplateNameLoc);
- Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
- Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template,
- Info, AtomicExpr->getSourceRange());
- if (Inst.isInvalid())
- return ExprError();
- // We do not want error diagnostics escaping here.
- Sema::SFINAETrap Trap(S);
- SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
- MLTAL);
- if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
- // C++2a [temp.constr.atomic]p1
- // ...If substitution results in an invalid type or expression, the
- // constraint is not satisfied.
- if (!Trap.hasErrorOccurred())
- // A non-SFINAE error has occured as a result of this
- // substitution.
- return ExprError();
-
- PartialDiagnosticAt SubstDiag{SourceLocation(),
- PartialDiagnostic::NullDiagnostic()};
- Info.takeSFINAEDiagnostic(SubstDiag);
- // FIXME: Concepts: This is an unfortunate consequence of there
- // being no serialization code for PartialDiagnostics and the fact
- // that serializing them would likely take a lot more storage than
- // just storing them as strings. We would still like, in the
- // future, to serialize the proper PartialDiagnostic as serializing
- // it as a string defeats the purpose of the diagnostic mechanism.
- SmallString<128> DiagString;
- DiagString = ": ";
- SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
- Satisfaction.Details.emplace_back(
- AtomicExpr,
- new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
- SubstDiag.first,
- std::string(DiagString.begin(), DiagString.end())});
- Satisfaction.IsSatisfied = false;
- return ExprEmpty();
- }
- }
-
- if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
- return ExprError();
-
- return SubstitutedExpression;
- });
-}
-
-template<typename TemplateDeclT>
-static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
- if (ConstraintExprs.empty()) {
- Satisfaction.IsSatisfied = true;
- return false;
- }
+ *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ // Atomic constraint - substitute arguments and check satisfaction.
+ ExprResult E;
+ {
+ TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());
+ InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),
+ InstantiatingTemplate::ConstraintSubstitution{},
+ NamedConcept, Info,
+ ConstraintExpr->getSourceRange());
+ if (Inst.isInvalid())
+ return true;
+ // We do not want error diagnostics escaping here.
+ Sema::SFINAETrap Trap(*this);
- for (auto& Arg : TemplateArgs)
- if (Arg.isInstantiationDependent()) {
- // No need to check satisfaction for dependent constraint expressions.
- Satisfaction.IsSatisfied = true;
+ E = SubstExpr(ConstraintExpr, MLTAL);
+ if (E.isInvalid() || Trap.hasErrorOccurred()) {
+ // C++2a [temp.constr.atomic]p1
+ // ...If substitution results in an invalid type or expression, the
+ // constraint is not satisfied.
+ IsSatisfied = false;
return false;
}
-
- Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
- Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
- TemplateIDRange);
- if (Inst.isInvalid())
- return true;
-
- MultiLevelTemplateArgumentList MLTAL;
- MLTAL.addOuterTemplateArguments(TemplateArgs);
-
- for (const Expr *ConstraintExpr : ConstraintExprs) {
- if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
- TemplateIDRange.getBegin(), MLTAL,
- ConstraintExpr, Satisfaction))
- return true;
- if (!Satisfaction.IsSatisfied)
- // [temp.constr.op] p2
- // [...] To determine if a conjunction is satisfied, the satisfaction
- // of the first operand is checked. If that is not satisfied, the
- // conjunction is not satisfied. [...]
- return false;
}
- return false;
-}
-
-bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
- return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- Satisfaction);
-}
-bool
-Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
- return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- Satisfaction);
-}
-
-bool
-Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
- return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- Satisfaction);
-}
-
-bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction) {
- return calculateConstraintSatisfaction(
- *this, ConstraintExpr, Satisfaction,
- [](const Expr *AtomicExpr) -> ExprResult {
- return ExprResult(const_cast<Expr *>(AtomicExpr));
- });
-}
-
-bool Sema::EnsureTemplateArgumentListConstraints(
- TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange) {
- ConstraintSatisfaction Satisfaction;
- llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
- TD->getAssociatedConstraints(AssociatedConstraints);
- if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs,
- TemplateIDRange, Satisfaction))
+ if (!CheckConstraintExpression(E.get()))
return true;
- if (!Satisfaction.IsSatisfied) {
- SmallString<128> TemplateArgString;
- TemplateArgString = " ";
- TemplateArgString += getTemplateArgumentBindingsText(
- TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
-
- Diag(TemplateIDRange.getBegin(),
- diag::err_template_arg_list_constraints_not_satisfied)
- << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD
- << TemplateArgString << TemplateIDRange;
- DiagnoseUnsatisfiedConstraint(Satisfaction);
+ SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
+ Expr::EvalResult EvalResult;
+ EvalResult.Diag = &EvaluationDiags;
+ if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {
+ // C++2a [temp.constr.atomic]p1
+ // ...E shall be a constant expression of type bool.
+ Diag(E.get()->getBeginLoc(),
+ diag::err_non_constant_constraint_expression)
+ << E.get()->getSourceRange();
+ for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
+ Diag(PDiag.first, PDiag.second);
return true;
}
- return false;
-}
-
-static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
- bool First = true) {
- SubstExpr = SubstExpr->IgnoreParenImpCasts();
- if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
- switch (BO->getOpcode()) {
- // These two cases will in practice only be reached when using fold
- // expressions with || and &&, since otherwise the || and && will have been
- // broken down into atomic constraints during satisfaction checking.
- case BO_LOr:
- // Or evaluated to false - meaning both RHS and LHS evaluated to false.
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
- /*First=*/false);
- return;
- case BO_LAnd:
- bool LHSSatisfied;
- BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context);
- if (LHSSatisfied) {
- // LHS is true, so RHS must be false.
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First);
- return;
- }
- // LHS is false
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First);
-
- // RHS might also be false
- bool RHSSatisfied;
- BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context);
- if (!RHSSatisfied)
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
- /*First=*/false);
- return;
- case BO_GE:
- case BO_LE:
- case BO_GT:
- case BO_LT:
- case BO_EQ:
- case BO_NE:
- if (BO->getLHS()->getType()->isIntegerType() &&
- BO->getRHS()->getType()->isIntegerType()) {
- Expr::EvalResult SimplifiedLHS;
- Expr::EvalResult SimplifiedRHS;
- BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context);
- BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context);
- if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
- S.Diag(SubstExpr->getBeginLoc(),
- diag::note_atomic_constraint_evaluated_to_false_elaborated)
- << (int)First << SubstExpr
- << SimplifiedLHS.Val.getInt().toString(10)
- << BinaryOperator::getOpcodeStr(BO->getOpcode())
- << SimplifiedRHS.Val.getInt().toString(10);
- return;
- }
- }
- break;
-
- default:
- break;
- }
- } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
- if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- S.Diag(
- CSE->getSourceRange().getBegin(),
- diag::
- note_single_arg_concept_specialization_constraint_evaluated_to_false)
- << (int)First
- << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
- << CSE->getNamedConcept();
- } else {
- S.Diag(SubstExpr->getSourceRange().getBegin(),
- diag::note_concept_specialization_constraint_evaluated_to_false)
- << (int)First << CSE;
- }
- S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
- return;
- }
-
- S.Diag(SubstExpr->getSourceRange().getBegin(),
- diag::note_atomic_constraint_evaluated_to_false)
- << (int)First << SubstExpr;
-}
-template<typename SubstitutionDiagnostic>
-static void diagnoseUnsatisfiedConstraintExpr(
- Sema &S, const Expr *E,
- const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,
- bool First = true) {
- if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()){
- S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
- << Diag->second;
- return;
- }
+ IsSatisfied = EvalResult.Val.getInt().getBoolValue();
- diagnoseWellFormedUnsatisfiedConstraintExpr(S,
- Record.template get<Expr *>(), First);
-}
-
-void Sema::DiagnoseUnsatisfiedConstraint(
- const ConstraintSatisfaction& Satisfaction) {
- assert(!Satisfaction.IsSatisfied &&
- "Attempted to diagnose a satisfied constraint");
- bool First = true;
- for (auto &Pair : Satisfaction.Details) {
- diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
- First = false;
- }
-}
-
-void Sema::DiagnoseUnsatisfiedConstraint(
- const ASTConstraintSatisfaction &Satisfaction) {
- assert(!Satisfaction.IsSatisfied &&
- "Attempted to diagnose a satisfied constraint");
- bool First = true;
- for (auto &Pair : Satisfaction) {
- diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
- First = false;
- }
+ return false;
} \ No newline at end of file
OpenPOWER on IntegriCloud