2019-10-15 15:24:26 +00:00
|
|
|
|
//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
|
|
|
|
|
//
|
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
|
//
|
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// This file implements semantic analysis for C++ constraints and concepts.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
#include "clang/Sema/SemaConcept.h"
|
2019-10-15 15:24:26 +00:00
|
|
|
|
#include "clang/Sema/Sema.h"
|
2019-12-06 01:30:21 +02:00
|
|
|
|
#include "clang/Sema/SemaInternal.h"
|
2019-10-15 15:24:26 +00:00
|
|
|
|
#include "clang/Sema/SemaDiagnostic.h"
|
|
|
|
|
#include "clang/Sema/TemplateDeduction.h"
|
|
|
|
|
#include "clang/Sema/Template.h"
|
2020-01-18 14:58:01 +02:00
|
|
|
|
#include "clang/AST/ExprCXX.h"
|
2019-12-23 08:37:35 +02:00
|
|
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
2020-01-09 15:07:51 +02:00
|
|
|
|
#include "clang/Basic/OperatorPrecedence.h"
|
2019-12-06 01:30:21 +02:00
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
|
#include "llvm/ADT/PointerUnion.h"
|
2019-10-15 15:24:26 +00:00
|
|
|
|
using namespace clang;
|
|
|
|
|
using namespace sema;
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
bool
|
|
|
|
|
Sema::CheckConstraintExpression(Expr *ConstraintExpression, Token NextToken,
|
|
|
|
|
bool *PossibleNonPrimary,
|
|
|
|
|
bool IsTrailingRequiresClause) {
|
2019-10-15 15:24:26 +00:00
|
|
|
|
// C++2a [temp.constr.atomic]p1
|
|
|
|
|
// ..E shall be a constant expression of type bool.
|
|
|
|
|
|
|
|
|
|
ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts();
|
|
|
|
|
|
|
|
|
|
if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
|
|
|
|
|
if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
|
2020-01-09 15:07:51 +02:00
|
|
|
|
return CheckConstraintExpression(BinOp->getLHS(), NextToken,
|
|
|
|
|
PossibleNonPrimary) &&
|
|
|
|
|
CheckConstraintExpression(BinOp->getRHS(), NextToken,
|
|
|
|
|
PossibleNonPrimary);
|
2019-10-15 15:24:26 +00:00
|
|
|
|
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
|
2020-01-09 15:07:51 +02:00
|
|
|
|
return CheckConstraintExpression(C->getSubExpr(), NextToken,
|
|
|
|
|
PossibleNonPrimary);
|
|
|
|
|
|
|
|
|
|
QualType Type = ConstraintExpression->getType();
|
|
|
|
|
|
|
|
|
|
auto CheckForNonPrimary = [&] {
|
|
|
|
|
if (PossibleNonPrimary)
|
|
|
|
|
*PossibleNonPrimary =
|
|
|
|
|
// We have the following case:
|
|
|
|
|
// template<typename> requires func(0) struct S { };
|
|
|
|
|
// The user probably isn't aware of the parentheses required around
|
|
|
|
|
// the function call, and we're only going to parse 'func' as the
|
|
|
|
|
// primary-expression, and complain that it is of non-bool type.
|
|
|
|
|
(NextToken.is(tok::l_paren) &&
|
|
|
|
|
(IsTrailingRequiresClause ||
|
|
|
|
|
(Type->isDependentType() &&
|
|
|
|
|
IsDependentFunctionNameExpr(ConstraintExpression)) ||
|
|
|
|
|
Type->isFunctionType() ||
|
|
|
|
|
Type->isSpecificBuiltinType(BuiltinType::Overload))) ||
|
|
|
|
|
// We have the following case:
|
|
|
|
|
// template<typename T> requires size_<T> == 0 struct S { };
|
|
|
|
|
// The user probably isn't aware of the parentheses required around
|
|
|
|
|
// the binary operator, and we're only going to parse 'func' as the
|
|
|
|
|
// first operand, and complain that it is of non-bool type.
|
|
|
|
|
getBinOpPrecedence(NextToken.getKind(),
|
|
|
|
|
/*GreaterThanIsOperator=*/true,
|
|
|
|
|
getLangOpts().CPlusPlus11) > prec::LogicalAnd;
|
|
|
|
|
};
|
2019-10-15 15:24:26 +00:00
|
|
|
|
|
|
|
|
|
// An atomic constraint!
|
2020-01-09 15:07:51 +02:00
|
|
|
|
if (ConstraintExpression->isTypeDependent()) {
|
|
|
|
|
CheckForNonPrimary();
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return true;
|
2020-01-09 15:07:51 +02:00
|
|
|
|
}
|
2019-10-15 15:24:26 +00:00
|
|
|
|
|
|
|
|
|
if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
|
|
|
|
|
Diag(ConstraintExpression->getExprLoc(),
|
|
|
|
|
diag::err_non_bool_atomic_constraint) << Type
|
|
|
|
|
<< ConstraintExpression->getSourceRange();
|
2020-01-09 15:07:51 +02:00
|
|
|
|
CheckForNonPrimary();
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-01-09 15:07:51 +02:00
|
|
|
|
|
|
|
|
|
if (PossibleNonPrimary)
|
|
|
|
|
*PossibleNonPrimary = false;
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
template <typename AtomicEvaluator>
|
|
|
|
|
static bool
|
|
|
|
|
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
|
|
|
|
ConstraintSatisfaction &Satisfaction,
|
|
|
|
|
AtomicEvaluator &&Evaluator) {
|
2019-10-15 15:24:26 +00:00
|
|
|
|
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
|
|
|
|
|
|
|
|
|
|
if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
|
|
|
|
|
if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction,
|
|
|
|
|
Evaluator))
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return true;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
|
|
|
|
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.
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return false;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
|
|
|
|
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.
|
2019-10-15 15:24:26 +00:00
|
|
|
|
return false;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
|
|
|
|
return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction,
|
|
|
|
|
std::forward<AtomicEvaluator>(Evaluator));
|
2019-10-15 15:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
|
2019-12-06 01:30:21 +02:00
|
|
|
|
return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
|
|
|
|
|
std::forward<AtomicEvaluator>(Evaluator));
|
2019-10-25 00:09:37 +03:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
// An atomic constraint expression
|
|
|
|
|
ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
|
2019-10-25 00:09:37 +03:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (SubstitutedAtomicExpr.isInvalid())
|
2019-10-25 00:09:37 +03:00
|
|
|
|
return true;
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (!SubstitutedAtomicExpr.isUsable())
|
|
|
|
|
// Evaluator has decided satisfaction without yielding an expression.
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
EnterExpressionEvaluationContext ConstantEvaluated(
|
|
|
|
|
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
2019-10-28 14:36:31 -07:00
|
|
|
|
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
|
|
|
|
|
Expr::EvalResult EvalResult;
|
|
|
|
|
EvalResult.Diag = &EvaluationDiags;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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();
|
2019-10-28 14:36:31 -07:00
|
|
|
|
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
|
2019-12-06 01:30:21 +02:00
|
|
|
|
S.Diag(PDiag.first, PDiag.second);
|
2019-10-25 00:09:37 +03:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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);
|
|
|
|
|
unsigned MessageSize = DiagString.size();
|
|
|
|
|
char *Mem = new (S.Context) char[MessageSize];
|
|
|
|
|
memcpy(Mem, DiagString.c_str(), MessageSize);
|
|
|
|
|
Satisfaction.Details.emplace_back(
|
|
|
|
|
AtomicExpr,
|
|
|
|
|
new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
|
|
|
|
|
SubstDiag.first, StringRef(Mem, MessageSize)});
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& Arg : TemplateArgs)
|
|
|
|
|
if (Arg.isInstantiationDependent()) {
|
|
|
|
|
// No need to check satisfaction for dependent constraint expressions.
|
|
|
|
|
Satisfaction.IsSatisfied = true;
|
|
|
|
|
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);
|
|
|
|
|
}
|
2019-10-25 00:09:37 +03:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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))
|
|
|
|
|
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);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-10-28 14:36:31 -07:00
|
|
|
|
return false;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diagnoseWellFormedUnsatisfiedConstraintExpr(S,
|
|
|
|
|
Record.template get<Expr *>(), First);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-18 14:58:01 +02:00
|
|
|
|
void Sema::DiagnoseUnsatisfiedConstraint(
|
|
|
|
|
const ConstraintSatisfaction& Satisfaction) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
assert(!Satisfaction.IsSatisfied &&
|
|
|
|
|
"Attempted to diagnose a satisfied constraint");
|
2020-01-18 14:58:01 +02:00
|
|
|
|
bool First = true;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
for (auto &Pair : Satisfaction.Details) {
|
|
|
|
|
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
|
|
|
|
First = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Sema::DiagnoseUnsatisfiedConstraint(
|
2020-01-18 14:58:01 +02:00
|
|
|
|
const ASTConstraintSatisfaction &Satisfaction) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
assert(!Satisfaction.IsSatisfied &&
|
|
|
|
|
"Attempted to diagnose a satisfied constraint");
|
2020-01-18 14:58:01 +02:00
|
|
|
|
bool First = true;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
for (auto &Pair : Satisfaction) {
|
|
|
|
|
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
|
|
|
|
First = false;
|
|
|
|
|
}
|
2019-12-23 08:37:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
const NormalizedConstraint *
|
|
|
|
|
Sema::getNormalizedAssociatedConstraints(
|
|
|
|
|
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) {
|
|
|
|
|
auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
|
|
|
|
|
if (CacheEntry == NormalizationCache.end()) {
|
|
|
|
|
auto Normalized =
|
|
|
|
|
NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl,
|
|
|
|
|
AssociatedConstraints);
|
|
|
|
|
CacheEntry =
|
|
|
|
|
NormalizationCache
|
|
|
|
|
.try_emplace(ConstrainedDecl,
|
|
|
|
|
Normalized
|
|
|
|
|
? new (Context) NormalizedConstraint(
|
|
|
|
|
std::move(*Normalized))
|
|
|
|
|
: nullptr)
|
|
|
|
|
.first;
|
2019-12-23 08:37:35 +02:00
|
|
|
|
}
|
2020-01-09 15:07:51 +02:00
|
|
|
|
return CacheEntry->second;
|
|
|
|
|
}
|
2019-12-23 08:37:35 +02:00
|
|
|
|
|
|
|
|
|
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
|
|
|
|
ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs,
|
|
|
|
|
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
|
|
|
|
|
if (!N.isAtomic()) {
|
|
|
|
|
if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs,
|
|
|
|
|
ArgsAsWritten))
|
|
|
|
|
return true;
|
|
|
|
|
return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs,
|
|
|
|
|
ArgsAsWritten);
|
|
|
|
|
}
|
|
|
|
|
TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
|
|
|
|
|
|
|
|
|
|
AtomicConstraint &Atomic = *N.getAtomicConstraint();
|
|
|
|
|
TemplateArgumentListInfo SubstArgs;
|
|
|
|
|
MultiLevelTemplateArgumentList MLTAL;
|
|
|
|
|
MLTAL.addOuterTemplateArguments(TemplateArgs);
|
|
|
|
|
if (!Atomic.ParameterMapping) {
|
|
|
|
|
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
|
|
|
|
|
S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
|
|
|
|
|
/*Depth=*/0, OccurringIndices);
|
2020-01-09 15:07:51 +02:00
|
|
|
|
Atomic.ParameterMapping.emplace(
|
|
|
|
|
MutableArrayRef<TemplateArgumentLoc>(
|
|
|
|
|
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()],
|
|
|
|
|
OccurringIndices.count()));
|
|
|
|
|
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
|
2019-12-23 08:37:35 +02:00
|
|
|
|
if (OccurringIndices[I])
|
2020-01-09 15:07:51 +02:00
|
|
|
|
new (&(*Atomic.ParameterMapping)[J++]) TemplateArgumentLoc(
|
2019-12-23 08:37:35 +02:00
|
|
|
|
S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I],
|
|
|
|
|
// Here we assume we do not support things like
|
|
|
|
|
// template<typename A, typename B>
|
|
|
|
|
// concept C = ...;
|
|
|
|
|
//
|
|
|
|
|
// template<typename... Ts> requires C<Ts...>
|
|
|
|
|
// struct S { };
|
|
|
|
|
// The above currently yields a diagnostic.
|
|
|
|
|
// We still might have default arguments for concept parameters.
|
|
|
|
|
ArgsAsWritten->NumTemplateArgs > I ?
|
|
|
|
|
ArgsAsWritten->arguments()[I].getLocation() :
|
|
|
|
|
SourceLocation()));
|
|
|
|
|
}
|
|
|
|
|
Sema::InstantiatingTemplate Inst(
|
|
|
|
|
S, ArgsAsWritten->arguments().front().getSourceRange().getBegin(),
|
|
|
|
|
Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, Concept,
|
|
|
|
|
SourceRange(ArgsAsWritten->arguments()[0].getSourceRange().getBegin(),
|
|
|
|
|
ArgsAsWritten->arguments().back().getSourceRange().getEnd()));
|
|
|
|
|
if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
|
|
|
|
|
return true;
|
|
|
|
|
std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
|
|
|
|
|
N.getAtomicConstraint()->ParameterMapping->begin());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
Optional<NormalizedConstraint>
|
|
|
|
|
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
|
|
|
|
|
ArrayRef<const Expr *> E) {
|
|
|
|
|
assert(E.size() != 0);
|
|
|
|
|
auto First = fromConstraintExpr(S, D, E[0]);
|
|
|
|
|
if (E.size() == 1)
|
|
|
|
|
return First;
|
|
|
|
|
auto Second = fromConstraintExpr(S, D, E[1]);
|
|
|
|
|
if (!Second)
|
|
|
|
|
return None;
|
|
|
|
|
llvm::Optional<NormalizedConstraint> Conjunction;
|
|
|
|
|
Conjunction.emplace(S.Context, std::move(*First), std::move(*Second),
|
|
|
|
|
CCK_Conjunction);
|
|
|
|
|
for (unsigned I = 2; I < E.size(); ++I) {
|
|
|
|
|
auto Next = fromConstraintExpr(S, D, E[I]);
|
|
|
|
|
if (!Next)
|
|
|
|
|
return llvm::Optional<NormalizedConstraint>{};
|
|
|
|
|
NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction),
|
|
|
|
|
std::move(*Next), CCK_Conjunction);
|
|
|
|
|
*Conjunction = std::move(NewConjunction);
|
|
|
|
|
}
|
|
|
|
|
return Conjunction;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-23 08:37:35 +02:00
|
|
|
|
llvm::Optional<NormalizedConstraint>
|
|
|
|
|
NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
|
|
|
|
assert(E != nullptr);
|
|
|
|
|
|
|
|
|
|
// C++ [temp.constr.normal]p1.1
|
|
|
|
|
// [...]
|
|
|
|
|
// - The normal form of an expression (E) is the normal form of E.
|
|
|
|
|
// [...]
|
|
|
|
|
E = E->IgnoreParenImpCasts();
|
|
|
|
|
if (auto *BO = dyn_cast<const BinaryOperator>(E)) {
|
|
|
|
|
if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
|
|
|
|
|
auto LHS = fromConstraintExpr(S, D, BO->getLHS());
|
|
|
|
|
if (!LHS)
|
|
|
|
|
return None;
|
|
|
|
|
auto RHS = fromConstraintExpr(S, D, BO->getRHS());
|
|
|
|
|
if (!RHS)
|
|
|
|
|
return None;
|
|
|
|
|
|
|
|
|
|
return NormalizedConstraint(
|
2020-01-09 15:07:51 +02:00
|
|
|
|
S.Context, std::move(*LHS), std::move(*RHS),
|
2019-12-23 08:37:35 +02:00
|
|
|
|
BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction);
|
|
|
|
|
}
|
|
|
|
|
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
|
2020-01-09 15:07:51 +02:00
|
|
|
|
const NormalizedConstraint *SubNF;
|
2019-12-23 08:37:35 +02:00
|
|
|
|
{
|
|
|
|
|
Sema::InstantiatingTemplate Inst(
|
|
|
|
|
S, CSE->getExprLoc(),
|
|
|
|
|
Sema::InstantiatingTemplate::ConstraintNormalization{}, D,
|
|
|
|
|
CSE->getSourceRange());
|
|
|
|
|
// C++ [temp.constr.normal]p1.1
|
|
|
|
|
// [...]
|
|
|
|
|
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
|
|
|
|
|
// where C names a concept, is the normal form of the
|
|
|
|
|
// constraint-expression of C, after substituting A1, A2, ..., AN for C’s
|
|
|
|
|
// respective template parameters in the parameter mappings in each atomic
|
|
|
|
|
// constraint. If any such substitution results in an invalid type or
|
|
|
|
|
// expression, the program is ill-formed; no diagnostic is required.
|
|
|
|
|
// [...]
|
2020-01-09 15:07:51 +02:00
|
|
|
|
ConceptDecl *CD = CSE->getNamedConcept();
|
|
|
|
|
SubNF = S.getNormalizedAssociatedConstraints(CD,
|
|
|
|
|
{CD->getConstraintExpr()});
|
2019-12-23 08:37:35 +02:00
|
|
|
|
if (!SubNF)
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
Optional<NormalizedConstraint> New;
|
|
|
|
|
New.emplace(S.Context, *SubNF);
|
|
|
|
|
|
2019-12-23 08:37:35 +02:00
|
|
|
|
if (substituteParameterMappings(
|
2020-01-09 15:07:51 +02:00
|
|
|
|
S, *New, CSE->getNamedConcept(),
|
2019-12-23 08:37:35 +02:00
|
|
|
|
CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten()))
|
|
|
|
|
return None;
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
return New;
|
2019-12-23 08:37:35 +02:00
|
|
|
|
}
|
|
|
|
|
return NormalizedConstraint{new (S.Context) AtomicConstraint(S, E)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using NormalForm =
|
|
|
|
|
llvm::SmallVector<llvm::SmallVector<AtomicConstraint *, 2>, 4>;
|
|
|
|
|
|
|
|
|
|
static NormalForm makeCNF(const NormalizedConstraint &Normalized) {
|
|
|
|
|
if (Normalized.isAtomic())
|
|
|
|
|
return {{Normalized.getAtomicConstraint()}};
|
|
|
|
|
|
|
|
|
|
NormalForm LCNF = makeCNF(Normalized.getLHS());
|
|
|
|
|
NormalForm RCNF = makeCNF(Normalized.getRHS());
|
|
|
|
|
if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) {
|
|
|
|
|
LCNF.reserve(LCNF.size() + RCNF.size());
|
|
|
|
|
while (!RCNF.empty())
|
|
|
|
|
LCNF.push_back(RCNF.pop_back_val());
|
|
|
|
|
return LCNF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disjunction
|
|
|
|
|
NormalForm Res;
|
|
|
|
|
Res.reserve(LCNF.size() * RCNF.size());
|
|
|
|
|
for (auto &LDisjunction : LCNF)
|
|
|
|
|
for (auto &RDisjunction : RCNF) {
|
|
|
|
|
NormalForm::value_type Combined;
|
|
|
|
|
Combined.reserve(LDisjunction.size() + RDisjunction.size());
|
|
|
|
|
std::copy(LDisjunction.begin(), LDisjunction.end(),
|
|
|
|
|
std::back_inserter(Combined));
|
|
|
|
|
std::copy(RDisjunction.begin(), RDisjunction.end(),
|
|
|
|
|
std::back_inserter(Combined));
|
|
|
|
|
Res.emplace_back(Combined);
|
|
|
|
|
}
|
|
|
|
|
return Res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NormalForm makeDNF(const NormalizedConstraint &Normalized) {
|
|
|
|
|
if (Normalized.isAtomic())
|
|
|
|
|
return {{Normalized.getAtomicConstraint()}};
|
|
|
|
|
|
|
|
|
|
NormalForm LDNF = makeDNF(Normalized.getLHS());
|
|
|
|
|
NormalForm RDNF = makeDNF(Normalized.getRHS());
|
|
|
|
|
if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
|
|
|
|
|
LDNF.reserve(LDNF.size() + RDNF.size());
|
|
|
|
|
while (!RDNF.empty())
|
|
|
|
|
LDNF.push_back(RDNF.pop_back_val());
|
|
|
|
|
return LDNF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Conjunction
|
|
|
|
|
NormalForm Res;
|
|
|
|
|
Res.reserve(LDNF.size() * RDNF.size());
|
|
|
|
|
for (auto &LConjunction : LDNF) {
|
|
|
|
|
for (auto &RConjunction : RDNF) {
|
|
|
|
|
NormalForm::value_type Combined;
|
|
|
|
|
Combined.reserve(LConjunction.size() + RConjunction.size());
|
|
|
|
|
std::copy(LConjunction.begin(), LConjunction.end(),
|
|
|
|
|
std::back_inserter(Combined));
|
|
|
|
|
std::copy(RConjunction.begin(), RConjunction.end(),
|
|
|
|
|
std::back_inserter(Combined));
|
|
|
|
|
Res.emplace_back(Combined);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Res;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 15:07:51 +02:00
|
|
|
|
template<typename AtomicSubsumptionEvaluator>
|
|
|
|
|
static bool subsumes(NormalForm PDNF, NormalForm QCNF,
|
|
|
|
|
AtomicSubsumptionEvaluator E) {
|
2019-12-23 08:37:35 +02:00
|
|
|
|
// C++ [temp.constr.order] p2
|
|
|
|
|
// Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
|
|
|
|
|
// disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
|
|
|
|
|
// the conjuctive normal form of Q, where [...]
|
|
|
|
|
for (const auto &Pi : PDNF) {
|
|
|
|
|
for (const auto &Qj : QCNF) {
|
|
|
|
|
// C++ [temp.constr.order] p2
|
|
|
|
|
// - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
|
|
|
|
|
// and only if there exists an atomic constraint Pia in Pi for which
|
|
|
|
|
// there exists an atomic constraint, Qjb, in Qj such that Pia
|
|
|
|
|
// subsumes Qjb.
|
|
|
|
|
bool Found = false;
|
|
|
|
|
for (const AtomicConstraint *Pia : Pi) {
|
|
|
|
|
for (const AtomicConstraint *Qjb : Qj) {
|
2020-01-09 15:07:51 +02:00
|
|
|
|
if (E(*Pia, *Qjb)) {
|
2019-12-23 08:37:35 +02:00
|
|
|
|
Found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (Found)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-01-09 15:07:51 +02:00
|
|
|
|
if (!Found)
|
2019-12-23 08:37:35 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-09 15:07:51 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename AtomicSubsumptionEvaluator>
|
|
|
|
|
static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
|
|
|
|
|
NamedDecl *DQ, ArrayRef<const Expr *> Q, bool &Subsumes,
|
|
|
|
|
AtomicSubsumptionEvaluator E) {
|
|
|
|
|
// C++ [temp.constr.order] p2
|
|
|
|
|
// In order to determine if a constraint P subsumes a constraint Q, P is
|
|
|
|
|
// transformed into disjunctive normal form, and Q is transformed into
|
|
|
|
|
// conjunctive normal form. [...]
|
|
|
|
|
auto *PNormalized = S.getNormalizedAssociatedConstraints(DP, P);
|
|
|
|
|
if (!PNormalized)
|
|
|
|
|
return true;
|
|
|
|
|
const NormalForm PDNF = makeDNF(*PNormalized);
|
|
|
|
|
|
|
|
|
|
auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q);
|
|
|
|
|
if (!QNormalized)
|
|
|
|
|
return true;
|
|
|
|
|
const NormalForm QCNF = makeCNF(*QNormalized);
|
|
|
|
|
|
|
|
|
|
Subsumes = subsumes(PDNF, QCNF, E);
|
2019-12-23 08:37:35 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
|
|
|
|
|
NamedDecl *D2, ArrayRef<const Expr *> AC2,
|
|
|
|
|
bool &Result) {
|
|
|
|
|
if (AC1.empty()) {
|
|
|
|
|
Result = AC2.empty();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (AC2.empty()) {
|
|
|
|
|
// TD1 has associated constraints and TD2 does not.
|
|
|
|
|
Result = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::pair<NamedDecl *, NamedDecl *> Key{D1, D2};
|
|
|
|
|
auto CacheEntry = SubsumptionCache.find(Key);
|
|
|
|
|
if (CacheEntry != SubsumptionCache.end()) {
|
|
|
|
|
Result = CacheEntry->second;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-01-09 15:07:51 +02:00
|
|
|
|
|
|
|
|
|
if (subsumes(*this, D1, AC1, D2, AC2, Result,
|
|
|
|
|
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
|
|
|
|
return A.subsumes(Context, B);
|
|
|
|
|
}))
|
2019-12-23 08:37:35 +02:00
|
|
|
|
return true;
|
|
|
|
|
SubsumptionCache.try_emplace(Key, Result);
|
|
|
|
|
return false;
|
2020-01-09 15:07:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
|
|
|
|
|
ArrayRef<const Expr *> AC1, NamedDecl *D2, ArrayRef<const Expr *> AC2) {
|
|
|
|
|
if (isSFINAEContext())
|
|
|
|
|
// No need to work here because our notes would be discarded.
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (AC1.empty() || AC2.empty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
auto NormalExprEvaluator =
|
|
|
|
|
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
|
|
|
|
return A.subsumes(Context, B);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Expr *AmbiguousAtomic1 = nullptr, *AmbiguousAtomic2 = nullptr;
|
|
|
|
|
auto IdenticalExprEvaluator =
|
|
|
|
|
[&] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
|
|
|
|
if (!A.hasMatchingParameterMapping(Context, B))
|
|
|
|
|
return false;
|
|
|
|
|
const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
|
|
|
|
|
if (EA == EB)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Not the same source level expression - are the expressions
|
|
|
|
|
// identical?
|
|
|
|
|
llvm::FoldingSetNodeID IDA, IDB;
|
|
|
|
|
EA->Profile(IDA, Context, /*Cannonical=*/true);
|
|
|
|
|
EB->Profile(IDB, Context, /*Cannonical=*/true);
|
|
|
|
|
if (IDA != IDB)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
AmbiguousAtomic1 = EA;
|
|
|
|
|
AmbiguousAtomic2 = EB;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// The subsumption checks might cause diagnostics
|
|
|
|
|
SFINAETrap Trap(*this);
|
|
|
|
|
auto *Normalized1 = getNormalizedAssociatedConstraints(D1, AC1);
|
|
|
|
|
if (!Normalized1)
|
|
|
|
|
return false;
|
|
|
|
|
const NormalForm DNF1 = makeDNF(*Normalized1);
|
|
|
|
|
const NormalForm CNF1 = makeCNF(*Normalized1);
|
|
|
|
|
|
|
|
|
|
auto *Normalized2 = getNormalizedAssociatedConstraints(D2, AC2);
|
|
|
|
|
if (!Normalized2)
|
|
|
|
|
return false;
|
|
|
|
|
const NormalForm DNF2 = makeDNF(*Normalized2);
|
|
|
|
|
const NormalForm CNF2 = makeCNF(*Normalized2);
|
|
|
|
|
|
|
|
|
|
bool Is1AtLeastAs2Normally = subsumes(DNF1, CNF2, NormalExprEvaluator);
|
|
|
|
|
bool Is2AtLeastAs1Normally = subsumes(DNF2, CNF1, NormalExprEvaluator);
|
|
|
|
|
bool Is1AtLeastAs2 = subsumes(DNF1, CNF2, IdenticalExprEvaluator);
|
|
|
|
|
bool Is2AtLeastAs1 = subsumes(DNF2, CNF1, IdenticalExprEvaluator);
|
|
|
|
|
if (Is1AtLeastAs2 == Is1AtLeastAs2Normally &&
|
|
|
|
|
Is2AtLeastAs1 == Is2AtLeastAs1Normally)
|
|
|
|
|
// Same result - no ambiguity was caused by identical atomic expressions.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A different result! Some ambiguous atomic constraint(s) caused a difference
|
|
|
|
|
assert(AmbiguousAtomic1 && AmbiguousAtomic2);
|
|
|
|
|
|
|
|
|
|
Diag(AmbiguousAtomic1->getBeginLoc(), diag::note_ambiguous_atomic_constraints)
|
|
|
|
|
<< AmbiguousAtomic1->getSourceRange();
|
|
|
|
|
Diag(AmbiguousAtomic2->getBeginLoc(),
|
|
|
|
|
diag::note_ambiguous_atomic_constraints_similar_expression)
|
|
|
|
|
<< AmbiguousAtomic2->getSourceRange();
|
|
|
|
|
return true;
|
|
|
|
|
}
|