aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp')
-rwxr-xr-xcontrib/llvm-project/clang/lib/Sema/SemaConcept.cpp924
1 files changed, 746 insertions, 178 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp b/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp
index 931c9e3e2738..88fc846c89e4 100755
--- a/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp
+++ b/contrib/llvm-project/clang/lib/Sema/SemaConcept.cpp
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -12,26 +11,32 @@
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaConcept.h"
-#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/Sema/Overload.h"
-#include "clang/Sema/Initialization.h"
-#include "clang/Sema/SemaInternal.h"
+#include "TreeTransform.h"
+#include "clang/AST/ASTLambda.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/OperatorPrecedence.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Initialization.h"
+#include "clang/Sema/Overload.h"
+#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringExtras.h"
+#include <optional>
using namespace clang;
using namespace sema;
namespace {
class LogicalBinOp {
+ SourceLocation Loc;
OverloadedOperatorKind Op = OO_None;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
@@ -42,12 +47,14 @@ public:
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
LHS = BO->getLHS();
RHS = BO->getRHS();
+ Loc = BO->getExprLoc();
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
// If OO is not || or && it might not have exactly 2 arguments.
if (OO->getNumArgs() == 2) {
Op = OO->getOperator();
LHS = OO->getArg(0);
RHS = OO->getArg(1);
+ Loc = OO->getOperatorLoc();
}
}
}
@@ -58,6 +65,26 @@ public:
const Expr *getLHS() const { return LHS; }
const Expr *getRHS() const { return RHS; }
+
+ ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) const {
+ return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS()));
+ }
+
+ ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS,
+ ExprResult RHS) const {
+ assert((isAnd() || isOr()) && "Not the right kind of op?");
+ assert((!LHS.isInvalid() && !RHS.isInvalid()) && "not good expressions?");
+
+ if (!LHS.isUsable() || !RHS.isUsable())
+ return ExprEmpty();
+
+ // We should just be able to 'normalize' these to the builtin Binary
+ // Operator, since that is how they are evaluated in constriant checks.
+ return BinaryOperator::Create(SemaRef.Context, LHS.get(), RHS.get(),
+ BinaryOperator::getOverloadedOpcode(Op),
+ SemaRef.Context.BoolTy, VK_PRValue,
+ OK_Ordinary, Loc, FPOptionsOverride{});
+ }
};
}
@@ -81,27 +108,35 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
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() &&
- isa<UnresolvedLookupExpr>(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;
+ if (!PossibleNonPrimary)
+ return;
+
+ *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.
+ //
+ // However, if we're in a lambda, this might also be:
+ // []<typename> requires var () {};
+ // Which also looks like a function call due to the lambda parentheses,
+ // but unlike the first case, isn't an error, so this check is skipped.
+ (NextToken.is(tok::l_paren) &&
+ (IsTrailingRequiresClause ||
+ (Type->isDependentType() &&
+ isa<UnresolvedLookupExpr>(ConstraintExpression) &&
+ !dyn_cast_if_present<LambdaScopeInfo>(getCurFunction())) ||
+ 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;
};
// An atomic constraint!
@@ -123,17 +158,39 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
return true;
}
+namespace {
+struct SatisfactionStackRAII {
+ Sema &SemaRef;
+ bool Inserted = false;
+ SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND,
+ const llvm::FoldingSetNodeID &FSNID)
+ : SemaRef(SemaRef) {
+ if (ND) {
+ SemaRef.PushSatisfactionStackEntry(ND, FSNID);
+ Inserted = true;
+ }
+ }
+ ~SatisfactionStackRAII() {
+ if (Inserted)
+ SemaRef.PopSatisfactionStackEntry();
+ }
+};
+} // namespace
+
template <typename AtomicEvaluator>
-static bool
+static ExprResult
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction,
AtomicEvaluator &&Evaluator) {
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
if (LogicalBinOp BO = ConstraintExpr) {
- if (calculateConstraintSatisfaction(S, BO.getLHS(), Satisfaction,
- Evaluator))
- return true;
+ size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
+ ExprResult LHSRes = calculateConstraintSatisfaction(
+ S, BO.getLHS(), Satisfaction, Evaluator);
+
+ if (LHSRes.isInvalid())
+ return ExprError();
bool IsLHSSatisfied = Satisfaction.IsSatisfied;
@@ -144,7 +201,8 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
// is checked. If that is satisfied, the disjunction is satisfied.
// Otherwise, the disjunction is satisfied if and only if the second
// operand is satisfied.
- return false;
+ // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
+ return LHSRes;
if (BO.isAnd() && !IsLHSSatisfied)
// [temp.constr.op] p2
@@ -153,12 +211,38 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
// 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 false;
+ // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
+ return LHSRes;
- return calculateConstraintSatisfaction(
+ ExprResult RHSRes = calculateConstraintSatisfaction(
S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
- } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
- return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
+ if (RHSRes.isInvalid())
+ return ExprError();
+
+ bool IsRHSSatisfied = Satisfaction.IsSatisfied;
+ // Current implementation adds diagnostic information about the falsity
+ // of each false atomic constraint expression when it evaluates them.
+ // When the evaluation results to `false || true`, the information
+ // generated during the evaluation of left-hand side is meaningless
+ // because the whole expression evaluates to true.
+ // The following code removes the irrelevant diagnostic information.
+ // FIXME: We should probably delay the addition of diagnostic information
+ // until we know the entire expression is false.
+ if (BO.isOr() && IsRHSSatisfied) {
+ auto EffectiveDetailEnd = Satisfaction.Details.begin();
+ std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
+ Satisfaction.Details.erase(EffectiveDetailEnd,
+ Satisfaction.Details.end());
+ }
+
+ return BO.recreateBinOp(S, LHSRes, RHSRes);
+ }
+
+ if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
+ // These aren't evaluated, so we don't care about cleanups, so we can just
+ // evaluate these as if the cleanups didn't exist.
+ return calculateConstraintSatisfaction(
+ S, C->getSubExpr(), Satisfaction,
std::forward<AtomicEvaluator>(Evaluator));
}
@@ -166,11 +250,35 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
if (SubstitutedAtomicExpr.isInvalid())
- return true;
+ return ExprError();
if (!SubstitutedAtomicExpr.isUsable())
// Evaluator has decided satisfaction without yielding an expression.
- return false;
+ return ExprEmpty();
+
+ // We don't have the ability to evaluate this, since it contains a
+ // RecoveryExpr, so we want to fail overload resolution. Otherwise,
+ // we'd potentially pick up a different overload, and cause confusing
+ // diagnostics. SO, add a failure detail that will cause us to make this
+ // overload set not viable.
+ if (SubstitutedAtomicExpr.get()->containsErrors()) {
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = true;
+
+ PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error);
+ SmallString<128> DiagString;
+ DiagString = ": ";
+ Msg.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(
+ ConstraintExpr,
+ new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+ SubstitutedAtomicExpr.get()->getBeginLoc(),
+ StringRef(Mem, MessageSize)});
+ return SubstitutedAtomicExpr;
+ }
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
@@ -187,7 +295,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
<< SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second);
- return true;
+ return ExprError();
}
assert(EvalResult.Val.isInt() &&
@@ -197,17 +305,42 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
Satisfaction.Details.emplace_back(ConstraintExpr,
SubstitutedAtomicExpr.get());
+ return SubstitutedAtomicExpr;
+}
+
+static bool
+DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
+ const NamedDecl *Templ, const Expr *E,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+ E->Profile(ID, S.Context, /*Canonical=*/true);
+ for (const auto &List : MLTAL)
+ for (const auto &TemplateArg : List.Args)
+ TemplateArg.Profile(ID, S.Context);
+
+ // Note that we have to do this with our own collection, because there are
+ // times where a constraint-expression check can cause us to need to evaluate
+ // other constriants that are unrelated, such as when evaluating a recovery
+ // expression, or when trying to determine the constexpr-ness of special
+ // members. Otherwise we could just use the
+ // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function.
+ if (S.SatisfactionStackContains(Templ, ID)) {
+ S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
+ << const_cast<Expr *>(E) << E->getSourceRange();
+ return true;
+ }
+
return false;
}
-static bool calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
- SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
- const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
+static ExprResult calculateConstraintSatisfaction(
+ Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
+ const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
+ ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
EnterExpressionEvaluationContext ConstantEvaluated(
- S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+ Sema::ReuseLambdaContextDecl);
// Atomic constraint - substitute arguments and check satisfaction.
ExprResult SubstitutedExpression;
@@ -219,23 +352,28 @@ static bool calculateConstraintSatisfaction(
AtomicExpr->getSourceRange());
if (Inst.isInvalid())
return ExprError();
+
+ llvm::FoldingSetNodeID ID;
+ if (Template &&
+ DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = true;
+ return ExprEmpty();
+ }
+
+ SatisfactionStackRAII StackRAII(S, Template, ID);
+
// We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S);
- SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
- MLTAL);
- // Substitution might have stripped off a contextual conversion to
- // bool if this is the operand of an '&&' or '||'. For example, we
- // might lose an lvalue-to-rvalue conversion here. If so, put it back
- // before we try to evaluate.
- if (!SubstitutedExpression.isInvalid())
- SubstitutedExpression =
- S.PerformContextuallyConvertToBool(SubstitutedExpression.get());
+ SubstitutedExpression =
+ S.SubstConstraintExpr(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
+ // A non-SFINAE error has occurred as a result of this
// substitution.
return ExprError();
@@ -266,109 +404,316 @@ static bool calculateConstraintSatisfaction(
if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
return ExprError();
+ // [temp.constr.atomic]p3: To determine if an atomic constraint is
+ // satisfied, the parameter mapping and template arguments are first
+ // substituted into its expression. If substitution results in an
+ // invalid type or expression, the constraint is not satisfied.
+ // Otherwise, the lvalue-to-rvalue conversion is performed if necessary,
+ // and E shall be a constant expression of type bool.
+ //
+ // Perform the L to R Value conversion if necessary. We do so for all
+ // non-PRValue categories, else we fail to extend the lifetime of
+ // temporaries, and that fails the constant expression check.
+ if (!SubstitutedExpression.get()->isPRValue())
+ SubstitutedExpression = ImplicitCastExpr::Create(
+ S.Context, SubstitutedExpression.get()->getType(),
+ CK_LValueToRValue, SubstitutedExpression.get(),
+ /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
+
return SubstitutedExpression;
});
}
-static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
+static bool CheckConstraintSatisfaction(
+ Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
+ llvm::SmallVectorImpl<Expr *> &Converted,
+ const MultiLevelTemplateArgumentList &TemplateArgsLists,
+ 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;
- }
+ if (TemplateArgsLists.isAnyArgInstantiationDependent()) {
+ // No need to check satisfaction for dependent constraint expressions.
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+ ArrayRef<TemplateArgument> TemplateArgs =
+ TemplateArgsLists.getNumSubstitutedLevels() > 0
+ ? TemplateArgsLists.getOutermost()
+ : ArrayRef<TemplateArgument> {};
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
const_cast<NamedDecl *>(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))
+ ExprResult Res = calculateConstraintSatisfaction(
+ S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
+ ConstraintExpr, Satisfaction);
+ if (Res.isInvalid())
return true;
- if (!Satisfaction.IsSatisfied)
+
+ Converted.push_back(Res.get());
+ if (!Satisfaction.IsSatisfied) {
+ // Backfill the 'converted' list with nulls so we can keep the Converted
+ // and unconverted lists in sync.
+ Converted.append(ConstraintExprs.size() - Converted.size(), nullptr);
// [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. [...]
+ // [...] 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(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange,
- ConstraintSatisfaction &OutSatisfaction) {
+ llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
+ const MultiLevelTemplateArgumentList &TemplateArgsLists,
+ SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
if (ConstraintExprs.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
+ if (!Template) {
+ return ::CheckConstraintSatisfaction(
+ *this, nullptr, ConstraintExprs, ConvertedConstraints,
+ TemplateArgsLists, TemplateIDRange, OutSatisfaction);
+ }
+
+ // A list of the template argument list flattened in a predictible manner for
+ // the purposes of caching. The ConstraintSatisfaction type is in AST so it
+ // has no access to the MultiLevelTemplateArgumentList, so this has to happen
+ // here.
+ llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
+ for (auto List : TemplateArgsLists)
+ FlattenedArgs.insert(FlattenedArgs.end(), List.Args.begin(),
+ List.Args.end());
llvm::FoldingSetNodeID ID;
+ ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
void *InsertPos;
- ConstraintSatisfaction *Satisfaction = nullptr;
- bool ShouldCache = LangOpts.ConceptSatisfactionCaching && Template;
- if (ShouldCache) {
- ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs);
- Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos);
- if (Satisfaction) {
- OutSatisfaction = *Satisfaction;
- return false;
- }
- Satisfaction = new ConstraintSatisfaction(Template, TemplateArgs);
- } else {
- Satisfaction = &OutSatisfaction;
+ if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
+ OutSatisfaction = *Cached;
+ return false;
}
+
+ auto Satisfaction =
+ std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- *Satisfaction)) {
- if (ShouldCache)
- delete Satisfaction;
+ ConvertedConstraints, TemplateArgsLists,
+ TemplateIDRange, *Satisfaction)) {
+ OutSatisfaction = *Satisfaction;
return true;
}
- if (ShouldCache) {
- // We cannot use InsertNode here because CheckConstraintSatisfaction might
- // have invalidated it.
- SatisfactionCache.InsertNode(Satisfaction);
- OutSatisfaction = *Satisfaction;
+ if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
+ // The evaluation of this constraint resulted in us trying to re-evaluate it
+ // recursively. This isn't really possible, except we try to form a
+ // RecoveryExpr as a part of the evaluation. If this is the case, just
+ // return the 'cached' version (which will have the same result), and save
+ // ourselves the extra-insert. If it ever becomes possible to legitimately
+ // recursively check a constraint, we should skip checking the 'inner' one
+ // above, and replace the cached version with this one, as it would be more
+ // specific.
+ OutSatisfaction = *Cached;
+ return false;
}
+
+ // Else we can simply add this satisfaction to the list.
+ OutSatisfaction = *Satisfaction;
+ // We cannot use InsertPos here because CheckConstraintSatisfaction might have
+ // invalidated it.
+ // Note that entries of SatisfactionCache are deleted in Sema's destructor.
+ SatisfactionCache.InsertNode(Satisfaction.release());
return false;
}
bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
- *this, ConstraintExpr, Satisfaction,
- [](const Expr *AtomicExpr) -> ExprResult {
- return ExprResult(const_cast<Expr *>(AtomicExpr));
- });
+ *this, ConstraintExpr, Satisfaction,
+ [this](const Expr *AtomicExpr) -> ExprResult {
+ // We only do this to immitate lvalue-to-rvalue conversion.
+ return PerformContextuallyConvertToBool(
+ const_cast<Expr *>(AtomicExpr));
+ })
+ .isInvalid();
+}
+
+bool Sema::addInstantiatedCapturesToScope(
+ FunctionDecl *Function, const FunctionDecl *PatternDecl,
+ LocalInstantiationScope &Scope,
+ const MultiLevelTemplateArgumentList &TemplateArgs) {
+ const auto *LambdaClass = cast<CXXMethodDecl>(Function)->getParent();
+ const auto *LambdaPattern = cast<CXXMethodDecl>(PatternDecl)->getParent();
+
+ unsigned Instantiated = 0;
+
+ auto AddSingleCapture = [&](const ValueDecl *CapturedPattern,
+ unsigned Index) {
+ ValueDecl *CapturedVar = LambdaClass->getCapture(Index)->getCapturedVar();
+ if (CapturedVar->isInitCapture())
+ Scope.InstantiatedLocal(CapturedPattern, CapturedVar);
+ };
+
+ for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) {
+ if (!CapturePattern.capturesVariable()) {
+ Instantiated++;
+ continue;
+ }
+ const ValueDecl *CapturedPattern = CapturePattern.getCapturedVar();
+ if (!CapturedPattern->isParameterPack()) {
+ AddSingleCapture(CapturedPattern, Instantiated++);
+ } else {
+ Scope.MakeInstantiatedLocalArgPack(CapturedPattern);
+ std::optional<unsigned> NumArgumentsInExpansion =
+ getNumArgumentsInExpansion(CapturedPattern->getType(), TemplateArgs);
+ if (!NumArgumentsInExpansion)
+ continue;
+ for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg)
+ AddSingleCapture(CapturedPattern, Instantiated++);
+ }
+ }
+ return false;
+}
+
+bool Sema::SetupConstraintScope(
+ FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
+ if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
+ FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
+ InstantiatingTemplate Inst(
+ *this, FD->getPointOfInstantiation(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
+ TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
+ SourceRange());
+ if (Inst.isInvalid())
+ return true;
+
+ // addInstantiatedParametersToScope creates a map of 'uninstantiated' to
+ // 'instantiated' parameters and adds it to the context. For the case where
+ // this function is a template being instantiated NOW, we also need to add
+ // the list of current template arguments to the list so that they also can
+ // be picked out of the map.
+ if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) {
+ MultiLevelTemplateArgumentList JustTemplArgs(FD, SpecArgs->asArray(),
+ /*Final=*/false);
+ if (addInstantiatedParametersToScope(
+ FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs))
+ return true;
+ }
+
+ // If this is a member function, make sure we get the parameters that
+ // reference the original primary template.
+ // We walk up the instantiated template chain so that nested lambdas get
+ // handled properly.
+ for (FunctionTemplateDecl *FromMemTempl =
+ PrimaryTemplate->getInstantiatedFromMemberTemplate();
+ FromMemTempl;
+ FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) {
+ if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
+ Scope, MLTAL))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||
+ FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) {
+ FunctionDecl *InstantiatedFrom =
+ FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization
+ ? FD->getInstantiatedFromMemberFunction()
+ : FD->getInstantiatedFromDecl();
+
+ InstantiatingTemplate Inst(
+ *this, FD->getPointOfInstantiation(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
+ TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
+ SourceRange());
+ if (Inst.isInvalid())
+ return true;
+
+ // Case where this was not a template, but instantiated as a
+ // child-function.
+ if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
+ return true;
+ }
+
+ return false;
+}
+
+// This function collects all of the template arguments for the purposes of
+// constraint-instantiation and checking.
+std::optional<MultiLevelTemplateArgumentList>
+Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
+ FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ LocalInstantiationScope &Scope) {
+ MultiLevelTemplateArgumentList MLTAL;
+
+ // Collect the list of template arguments relative to the 'primary' template.
+ // We need the entire list, since the constraint is completely uninstantiated
+ // at this point.
+ MLTAL = getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
+ /*Final=*/false, /*Innermost=*/nullptr,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+ if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
+ return std::nullopt;
+
+ return MLTAL;
}
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction,
- SourceLocation UsageLoc) {
- const Expr *RC = FD->getTrailingRequiresClause();
- if (RC->isInstantiationDependent()) {
+ SourceLocation UsageLoc,
+ bool ForOverloadResolution) {
+ // Don't check constraints if the function is dependent. Also don't check if
+ // this is a function template specialization, as the call to
+ // CheckinstantiatedFunctionTemplateConstraints after this will check it
+ // better.
+ if (FD->isDependentContext() ||
+ FD->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
Satisfaction.IsSatisfied = true;
return false;
}
+
+ // A lambda conversion operator has the same constraints as the call operator
+ // and constraints checking relies on whether we are in a lambda call operator
+ // (and may refer to its parameters), so check the call operator instead.
+ if (const auto *MD = dyn_cast<CXXConversionDecl>(FD);
+ MD && isLambdaConversionOperator(const_cast<CXXConversionDecl *>(MD)))
+ return CheckFunctionConstraints(MD->getParent()->getLambdaCallOperator(),
+ Satisfaction, UsageLoc,
+ ForOverloadResolution);
+
+ DeclContext *CtxToSave = const_cast<FunctionDecl *>(FD);
+
+ while (isLambdaCallOperator(CtxToSave) || FD->isTransparentContext()) {
+ if (isLambdaCallOperator(CtxToSave))
+ CtxToSave = CtxToSave->getParent()->getParent();
+ else
+ CtxToSave = CtxToSave->getNonTransparentContext();
+ }
+
+ ContextRAII SavedContext{*this, CtxToSave};
+ LocalInstantiationScope Scope(*this, !ForOverloadResolution);
+ std::optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ const_cast<FunctionDecl *>(FD), {}, Scope);
+
+ if (!MLTAL)
+ return true;
+
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
@@ -376,22 +721,149 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
Record = const_cast<CXXRecordDecl *>(Method->getParent());
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
- // We substitute with empty arguments in order to rebuild the atomic
- // constraint in a constant-evaluated context.
- // FIXME: Should this be a dedicated TreeTransform?
+
+ LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
+ *this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope,
+ ForOverloadResolution);
+
return CheckConstraintSatisfaction(
- FD, {RC}, /*TemplateArgs=*/{},
+ FD, {FD->getTrailingRequiresClause()}, *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction);
}
+
+// Figure out the to-translation-unit depth for this function declaration for
+// the purpose of seeing if they differ by constraints. This isn't the same as
+// getTemplateDepth, because it includes already instantiated parents.
+static unsigned
+CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
+ bool SkipForSpecialization = false) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ ND, ND->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/nullptr,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+ return MLTAL.getNumLevels();
+}
+
+namespace {
+ class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
+ unsigned TemplateDepth = 0;
+ public:
+ using inherited = TreeTransform<AdjustConstraintDepth>;
+ AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
+ : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
+
+ using inherited::TransformTemplateTypeParmType;
+ QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+ TemplateTypeParmTypeLoc TL, bool) {
+ const TemplateTypeParmType *T = TL.getTypePtr();
+
+ TemplateTypeParmDecl *NewTTPDecl = nullptr;
+ if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
+ NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
+ TransformDecl(TL.getNameLoc(), OldTTPDecl));
+
+ QualType Result = getSema().Context.getTemplateTypeParmType(
+ T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
+ NewTTPDecl);
+ TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
+ NewTL.setNameLoc(TL.getNameLoc());
+ return Result;
+ }
+ };
+} // namespace
+
+static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
+ Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
+ const Expr *ConstrExpr) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false,
+ /*Innermost=*/nullptr,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
+ /*SkipForSpecialization*/ false);
+
+ if (MLTAL.getNumSubstitutedLevels() == 0)
+ return ConstrExpr;
+
+ Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
+
+ Sema::InstantiatingTemplate Inst(
+ S, DeclInfo.getLocation(),
+ Sema::InstantiatingTemplate::ConstraintNormalization{},
+ const_cast<NamedDecl *>(DeclInfo.getDecl()), SourceRange{});
+ if (Inst.isInvalid())
+ return nullptr;
+
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ if (auto *RD = dyn_cast<CXXRecordDecl>(DeclInfo.getDeclContext()))
+ ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
+ ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction(
+ const_cast<clang::Expr *>(ConstrExpr), MLTAL);
+ if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
+ return nullptr;
+ return SubstConstr.get();
+}
+
+bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
+ const Expr *OldConstr,
+ const TemplateCompareNewDeclInfo &New,
+ const Expr *NewConstr) {
+ if (OldConstr == NewConstr)
+ return true;
+ // C++ [temp.constr.decl]p4
+ if (Old && !New.isInvalid() && !New.ContainsDecl(Old) &&
+ Old->getLexicalDeclContext() != New.getLexicalDeclContext()) {
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpressionWithoutSatisfaction(*this, Old,
+ OldConstr))
+ OldConstr = SubstConstr;
+ else
+ return false;
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpressionWithoutSatisfaction(*this, New,
+ NewConstr))
+ NewConstr = SubstConstr;
+ else
+ return false;
+ }
+
+ llvm::FoldingSetNodeID ID1, ID2;
+ OldConstr->Profile(ID1, Context, /*Canonical=*/true);
+ NewConstr->Profile(ID2, Context, /*Canonical=*/true);
+ return ID1 == ID2;
+}
+
+bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
+ assert(FD->getFriendObjectKind() && "Must be a friend!");
+
+ // The logic for non-templates is handled in ASTContext::isSameEntity, so we
+ // don't have to bother checking 'DependsOnEnclosingTemplate' for a
+ // non-function-template.
+ assert(FD->getDescribedFunctionTemplate() &&
+ "Non-function templates don't need to be checked");
+
+ SmallVector<const Expr *, 3> ACs;
+ FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
+
+ unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
+ for (const Expr *Constraint : ACs)
+ if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth,
+ Constraint))
+ return true;
+
+ return false;
+}
+
bool Sema::EnsureTemplateArgumentListConstraints(
- TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
+ TemplateDecl *TD, const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange) {
ConstraintSatisfaction Satisfaction;
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
TD->getAssociatedConstraints(AssociatedConstraints);
- if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs,
+ if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgsLists,
TemplateIDRange, Satisfaction))
return true;
@@ -399,7 +871,8 @@ bool Sema::EnsureTemplateArgumentListConstraints(
SmallString<128> TemplateArgString;
TemplateArgString = " ";
TemplateArgString += getTemplateArgumentBindingsText(
- TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+ TD->getTemplateParameters(), TemplateArgsLists.getInnermost().data(),
+ TemplateArgsLists.getInnermost().size());
Diag(TemplateIDRange.getBegin(),
diag::err_template_arg_list_constraints_not_satisfied)
@@ -411,6 +884,49 @@ bool Sema::EnsureTemplateArgumentListConstraints(
return false;
}
+bool Sema::CheckInstantiatedFunctionTemplateConstraints(
+ SourceLocation PointOfInstantiation, FunctionDecl *Decl,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ ConstraintSatisfaction &Satisfaction) {
+ // In most cases we're not going to have constraints, so check for that first.
+ FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
+ // Note - code synthesis context for the constraints check is created
+ // inside CheckConstraintsSatisfaction.
+ SmallVector<const Expr *, 3> TemplateAC;
+ Template->getAssociatedConstraints(TemplateAC);
+ if (TemplateAC.empty()) {
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ // Enter the scope of this instantiation. We don't use
+ // PushDeclContext because we don't have a scope.
+ Sema::ContextRAII savedContext(*this, Decl);
+ LocalInstantiationScope Scope(*this);
+
+ std::optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
+ Scope);
+
+ if (!MLTAL)
+ return true;
+
+ Qualifiers ThisQuals;
+ CXXRecordDecl *Record = nullptr;
+ if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
+ ThisQuals = Method->getMethodQualifiers();
+ Record = Method->getParent();
+ }
+
+ CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
+ LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
+ *this, const_cast<FunctionDecl *>(Decl), *MLTAL, Scope);
+
+ llvm::SmallVector<Expr *, 1> Converted;
+ return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
+ PointOfInstantiation, Satisfaction);
+}
+
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::ExprRequirement *Req,
bool First) {
@@ -461,7 +977,7 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
Expr *e = Req->getExpr();
S.Diag(e->getBeginLoc(),
diag::note_expr_requirement_constraints_not_satisfied_simple)
- << (int)First << S.getDecltypeForParenthesizedExpr(e)
+ << (int)First << S.Context.getReferenceQualifiedType(e)
<< ConstraintExpr->getNamedConcept();
} else {
S.Diag(ConstraintExpr->getBeginLoc(),
@@ -502,31 +1018,28 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
return;
}
}
+static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
+ Expr *SubstExpr,
+ bool First = true);
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
- if (Req->isSubstitutionFailure()) {
- concepts::Requirement::SubstitutionDiagnostic *SubstDiag =
- Req->getSubstitutionDiagnostic();
- if (!SubstDiag->DiagMessage.empty())
- S.Diag(SubstDiag->DiagLoc,
- diag::note_nested_requirement_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity
- << SubstDiag->DiagMessage;
+ using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+ for (auto &Pair : Req->getConstraintSatisfaction()) {
+ if (auto *SubstDiag = Pair.second.dyn_cast<SubstitutionDiagnostic *>())
+ S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
+ << (int)First << Req->getInvalidConstraintEntity() << SubstDiag->second;
else
- S.Diag(SubstDiag->DiagLoc,
- diag::note_nested_requirement_unknown_substitution_error)
- << (int)First << SubstDiag->SubstitutedEntity;
- return;
+ diagnoseWellFormedUnsatisfiedConstraintExpr(
+ S, Pair.second.dyn_cast<Expr *>(), First);
+ First = false;
}
- S.DiagnoseUnsatisfiedConstraint(Req->getConstraintSatisfaction(), First);
}
-
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
Expr *SubstExpr,
- bool First = true) {
+ bool First) {
SubstExpr = SubstExpr->IgnoreParenImpCasts();
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
switch (BO->getOpcode()) {
@@ -606,6 +1119,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
return;
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
+ // FIXME: RequiresExpr should store dependent diagnostics.
for (concepts::Requirement *Req : RE->getRequirements())
if (!Req->isDependent() && !Req->isSatisfied()) {
if (auto *E = dyn_cast<concepts::ExprRequirement>(Req))
@@ -665,6 +1179,11 @@ void Sema::DiagnoseUnsatisfiedConstraint(
const NormalizedConstraint *
Sema::getNormalizedAssociatedConstraints(
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) {
+ // In case the ConstrainedDecl comes from modules, it is necessary to use
+ // the canonical decl to avoid different atomic constraints with the 'same'
+ // declarations.
+ ConstrainedDecl = cast<NamedDecl>(ConstrainedDecl->getCanonicalDecl());
+
auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
if (CacheEntry == NormalizationCache.end()) {
auto Normalized =
@@ -682,34 +1201,33 @@ Sema::getNormalizedAssociatedConstraints(
return CacheEntry->second;
}
-static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs,
- const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+static bool
+substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+ ConceptDecl *Concept,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten) {
if (!N.isAtomic()) {
- if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs,
+ if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
ArgsAsWritten))
return true;
- return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs,
+ return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
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);
- Atomic.ParameterMapping.emplace(
- MutableArrayRef<TemplateArgumentLoc>(
- new (S.Context) TemplateArgumentLoc[OccurringIndices.count()],
- OccurringIndices.count()));
+ TemplateArgumentLoc *TempArgs =
+ new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
if (OccurringIndices[I])
- new (&(*Atomic.ParameterMapping)[J++]) TemplateArgumentLoc(
- S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I],
+ new (&(TempArgs)[J++])
+ TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc(
+ TemplateParams->begin()[I],
// Here we assume we do not support things like
// template<typename A, typename B>
// concept C = ...;
@@ -718,44 +1236,59 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
// 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()));
+ ArgsAsWritten->NumTemplateArgs > I
+ ? ArgsAsWritten->arguments()[I].getLocation()
+ : SourceLocation()));
+ Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count());
}
Sema::InstantiatingTemplate Inst(
S, ArgsAsWritten->arguments().front().getSourceRange().getBegin(),
Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, Concept,
- SourceRange(ArgsAsWritten->arguments()[0].getSourceRange().getBegin(),
- ArgsAsWritten->arguments().back().getSourceRange().getEnd()));
+ ArgsAsWritten->arguments().front().getSourceRange());
if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
return true;
- Atomic.ParameterMapping.emplace(
- MutableArrayRef<TemplateArgumentLoc>(
- new (S.Context) TemplateArgumentLoc[SubstArgs.size()],
- SubstArgs.size()));
+
+ TemplateArgumentLoc *TempArgs =
+ new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
- N.getAtomicConstraint()->ParameterMapping->begin());
+ TempArgs);
+ Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
return false;
}
-Optional<NormalizedConstraint>
+static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+ const ConceptSpecializationExpr *CSE) {
+ TemplateArgumentList TAL{TemplateArgumentList::OnStack,
+ CSE->getTemplateArguments()};
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
+ /*Final=*/false, &TAL,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+
+ return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
+ CSE->getTemplateArgsAsWritten());
+}
+
+std::optional<NormalizedConstraint>
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
ArrayRef<const Expr *> E) {
assert(E.size() != 0);
auto Conjunction = fromConstraintExpr(S, D, E[0]);
if (!Conjunction)
- return None;
+ return std::nullopt;
for (unsigned I = 1; I < E.size(); ++I) {
auto Next = fromConstraintExpr(S, D, E[I]);
if (!Next)
- return None;
+ return std::nullopt;
*Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction),
std::move(*Next), CCK_Conjunction);
}
return Conjunction;
}
-llvm::Optional<NormalizedConstraint>
+std::optional<NormalizedConstraint>
NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
assert(E != nullptr);
@@ -764,13 +1297,19 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
// - The normal form of an expression (E) is the normal form of E.
// [...]
E = E->IgnoreParenImpCasts();
+
+ // C++2a [temp.param]p4:
+ // [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
+ // Fold expression is considered atomic constraints per current wording.
+ // See http://cplusplus.github.io/concepts-ts/ts-active.html#28
+
if (LogicalBinOp BO = E) {
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
if (!LHS)
- return None;
+ return std::nullopt;
auto RHS = fromConstraintExpr(S, D, BO.getRHS());
if (!RHS)
- return None;
+ return std::nullopt;
return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
@@ -794,16 +1333,14 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
SubNF = S.getNormalizedAssociatedConstraints(CD,
{CD->getConstraintExpr()});
if (!SubNF)
- return None;
+ return std::nullopt;
}
- Optional<NormalizedConstraint> New;
+ std::optional<NormalizedConstraint> New;
New.emplace(S.Context, *SubNF);
- if (substituteParameterMappings(
- S, *New, CSE->getNamedConcept(),
- CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten()))
- return None;
+ if (substituteParameterMappings(S, *New, CSE))
+ return std::nullopt;
return New;
}
@@ -873,7 +1410,7 @@ static NormalForm makeDNF(const NormalizedConstraint &Normalized) {
}
template<typename AtomicSubsumptionEvaluator>
-static bool subsumes(NormalForm PDNF, NormalForm QCNF,
+static bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
AtomicSubsumptionEvaluator E) {
// C++ [temp.constr.order] p2
// Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
@@ -926,9 +1463,26 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
return false;
}
-bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
- NamedDecl *D2, ArrayRef<const Expr *> AC2,
+bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
+ MutableArrayRef<const Expr *> AC1,
+ NamedDecl *D2,
+ MutableArrayRef<const Expr *> AC2,
bool &Result) {
+ if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) {
+ auto IsExpectedEntity = [](const FunctionDecl *FD) {
+ FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
+ return Kind == FunctionDecl::TK_NonTemplate ||
+ Kind == FunctionDecl::TK_FunctionTemplate;
+ };
+ const auto *FD2 = dyn_cast<FunctionDecl>(D2);
+ (void)IsExpectedEntity;
+ (void)FD1;
+ (void)FD2;
+ assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
+ "use non-instantiated function declaration for constraints partial "
+ "ordering");
+ }
+
if (AC1.empty()) {
Result = AC2.empty();
return false;
@@ -946,6 +1500,21 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
return false;
}
+ unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true);
+ unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true);
+
+ for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
+ if (Depth2 > Depth1) {
+ AC1[I] = AdjustConstraintDepth(*this, Depth2 - Depth1)
+ .TransformExpr(const_cast<Expr *>(AC1[I]))
+ .get();
+ } else if (Depth1 > Depth2) {
+ AC2[I] = AdjustConstraintDepth(*this, Depth1 - Depth2)
+ .TransformExpr(const_cast<Expr *>(AC2[I]))
+ .get();
+ }
+ }
+
if (subsumes(*this, D1, AC1, D2, AC2, Result,
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
return A.subsumes(Context, B);
@@ -981,8 +1550,8 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
// 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);
+ EA->Profile(IDA, Context, /*Canonical=*/true);
+ EB->Profile(IDB, Context, /*Canonical=*/true);
if (IDA != IDB)
return false;
@@ -1059,20 +1628,19 @@ concepts::ExprRequirement::ExprRequirement(
concepts::ExprRequirement::ReturnTypeRequirement::
ReturnTypeRequirement(TemplateParameterList *TPL) :
- TypeConstraintInfo(TPL, 0) {
+ TypeConstraintInfo(TPL, false) {
assert(TPL->size() == 1);
const TypeConstraint *TC =
cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint();
assert(TC &&
"TPL must have a template type parameter with a type constraint");
auto *Constraint =
- cast_or_null<ConceptSpecializationExpr>(
- TC->getImmediatelyDeclaredConstraint());
+ cast<ConceptSpecializationExpr>(TC->getImmediatelyDeclaredConstraint());
bool Dependent =
Constraint->getTemplateArgsAsWritten() &&
TemplateSpecializationType::anyInstantiationDependentTemplateArguments(
Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1));
- TypeConstraintInfo.setInt(Dependent ? 1 : 0);
+ TypeConstraintInfo.setInt(Dependent ? true : false);
}
concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) :