2019-10-15 15:24:26 +00:00
|
|
|
|
//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
|
|
|
|
|
//
|
2021-08-05 03:16:17 +00:00
|
|
|
|
// 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
|
2019-10-15 15:24:26 +00:00
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// This file implements semantic analysis for C++ constraints and concepts.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
#include "TreeTransform.h"
|
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 09:11:43 +02:00
|
|
|
|
#include "clang/Sema/Overload.h"
|
|
|
|
|
#include "clang/Sema/Initialization.h"
|
2022-08-19 13:57:45 -07:00
|
|
|
|
#include "clang/AST/ASTLambda.h"
|
2020-01-18 09:11:43 +02:00
|
|
|
|
#include "clang/AST/ExprConcepts.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"
|
2021-06-11 13:19:00 +01:00
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2023-01-14 11:07:21 -08:00
|
|
|
|
#include <optional>
|
2021-06-11 13:19:00 +01:00
|
|
|
|
|
2019-10-15 15:24:26 +00:00
|
|
|
|
using namespace clang;
|
|
|
|
|
using namespace sema;
|
|
|
|
|
|
2020-05-12 13:14:32 -07:00
|
|
|
|
namespace {
|
|
|
|
|
class LogicalBinOp {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
SourceLocation Loc;
|
2020-05-12 13:14:32 -07:00
|
|
|
|
OverloadedOperatorKind Op = OO_None;
|
|
|
|
|
const Expr *LHS = nullptr;
|
|
|
|
|
const Expr *RHS = nullptr;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LogicalBinOp(const Expr *E) {
|
|
|
|
|
if (auto *BO = dyn_cast<BinaryOperator>(E)) {
|
|
|
|
|
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
|
|
|
|
|
LHS = BO->getLHS();
|
|
|
|
|
RHS = BO->getRHS();
|
2022-08-19 13:57:45 -07:00
|
|
|
|
Loc = BO->getExprLoc();
|
2020-05-12 13:14:32 -07:00
|
|
|
|
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
|
2021-06-28 09:00:45 -04:00
|
|
|
|
// 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);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
Loc = OO->getOperatorLoc();
|
2021-06-28 09:00:45 -04:00
|
|
|
|
}
|
2020-05-12 13:14:32 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isAnd() const { return Op == OO_AmpAmp; }
|
|
|
|
|
bool isOr() const { return Op == OO_PipePipe; }
|
|
|
|
|
explicit operator bool() const { return isAnd() || isOr(); }
|
|
|
|
|
|
|
|
|
|
const Expr *getLHS() const { return LHS; }
|
|
|
|
|
const Expr *getRHS() const { return RHS; }
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
|
|
|
|
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{});
|
|
|
|
|
}
|
2020-05-12 13:14:32 -07:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Sema::CheckConstraintExpression(const 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();
|
|
|
|
|
|
2020-05-12 13:14:32 -07:00
|
|
|
|
if (LogicalBinOp BO = ConstraintExpression) {
|
|
|
|
|
return CheckConstraintExpression(BO.getLHS(), NextToken,
|
|
|
|
|
PossibleNonPrimary) &&
|
|
|
|
|
CheckConstraintExpression(BO.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 = [&] {
|
2023-03-17 10:13:02 +02:00
|
|
|
|
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;
|
2020-01-09 15:07:51 +02:00
|
|
|
|
};
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 08:43:08 -07:00
|
|
|
|
namespace {
|
|
|
|
|
struct SatisfactionStackRAII {
|
|
|
|
|
Sema &SemaRef;
|
2023-01-27 11:06:07 -08:00
|
|
|
|
bool Inserted = false;
|
|
|
|
|
SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND,
|
[NFC][clang] Fix static analyzer tool remarks about large copies by values
Reported by Coverity:
Big parameter passed by value
Copying large values is inefficient, consider passing by reference; Low, medium, and high size thresholds for detection can be adjusted.
1. Inside "SemaConcept.cpp" file, in subsumes<clang::Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(clang::NamedDecl *, llvm::ArrayRef<clang::Expr const *>, clang::NamedDecl *, llvm::ArrayRef<clang::Expr const *>)::[lambda(clang::AtomicConstraint const &, clang::AtomicConstraint const &) (instance 2)]>(llvm::SmallVector<llvm::SmallVector<clang::AtomicConstraint *, 2u>, 4u>, llvm::SmallVector<llvm::SmallVector<clang::AtomicConstraint *, 2u>, 4u>, T1): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter PDNF of type NormalForm (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
ii. pass_by_value: Passing parameter QCNF of type NormalForm (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
2. Inside "CodeGenAction.cpp" file, in clang::reportOptRecordError(llvm::Error, clang::DiagnosticsEngine &, clang::CodeGenOptions): A very large function call parameter exceeding the high threshold is passed by value.
i. pass_by_value: Passing parameter CodeGenOpts of type clang::CodeGenOptions const (size 1560 bytes) by value, which exceeds the high threshold of 512 bytes.
3. Inside "SemaCodeComplete.cpp" file, in HandleCodeCompleteResults(clang::Sema *, clang::CodeCompleteConsumer *, clang::CodeCompletionContext, clang::CodeCompletionResult *, unsigned int): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter Context of type clang::CodeCompletionContext (size 200 bytes) by value, which exceeds the low threshold of 128 bytes.
4. Inside "SemaConcept.cpp" file, in <unnamed>::SatisfactionStackRAII::SatisfactionStackRAII(clang::Sema &, clang::NamedDecl const *, llvm::FoldingSetNodeID): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter FSNID of type llvm::FoldingSetNodeID (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
Reviewed By: erichkeane, aaron.ballman
Differential Revision: https://reviews.llvm.org/D147708
2023-04-07 16:37:56 -04:00
|
|
|
|
const llvm::FoldingSetNodeID &FSNID)
|
2022-10-31 08:43:08 -07:00
|
|
|
|
: SemaRef(SemaRef) {
|
2023-01-27 11:06:07 -08:00
|
|
|
|
if (ND) {
|
|
|
|
|
SemaRef.PushSatisfactionStackEntry(ND, FSNID);
|
|
|
|
|
Inserted = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
~SatisfactionStackRAII() {
|
|
|
|
|
if (Inserted)
|
|
|
|
|
SemaRef.PopSatisfactionStackEntry();
|
2022-10-31 08:43:08 -07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
template <typename AtomicEvaluator>
|
2022-08-19 13:57:45 -07:00
|
|
|
|
static ExprResult
|
2019-12-06 01:30:21 +02:00
|
|
|
|
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
|
|
|
|
ConstraintSatisfaction &Satisfaction,
|
|
|
|
|
AtomicEvaluator &&Evaluator) {
|
2019-10-15 15:24:26 +00:00
|
|
|
|
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
|
|
|
|
|
|
2020-05-12 13:14:32 -07:00
|
|
|
|
if (LogicalBinOp BO = ConstraintExpr) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
ExprResult LHSRes = calculateConstraintSatisfaction(
|
|
|
|
|
S, BO.getLHS(), Satisfaction, Evaluator);
|
|
|
|
|
|
|
|
|
|
if (LHSRes.isInvalid())
|
|
|
|
|
return ExprError();
|
2020-05-12 13:14:32 -07:00
|
|
|
|
|
|
|
|
|
bool IsLHSSatisfied = Satisfaction.IsSatisfied;
|
|
|
|
|
|
|
|
|
|
if (BO.isOr() && 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.
|
2022-11-29 14:05:53 +01:00
|
|
|
|
// LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
|
|
|
|
|
return LHSRes;
|
2020-05-12 13:14:32 -07:00
|
|
|
|
|
|
|
|
|
if (BO.isAnd() && !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.
|
2022-11-29 14:05:53 +01:00
|
|
|
|
// LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
|
|
|
|
|
return LHSRes;
|
2022-07-01 11:21:21 -07:00
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
ExprResult RHSRes = calculateConstraintSatisfaction(
|
2022-08-19 12:37:04 -07:00
|
|
|
|
S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
|
2022-08-19 13:57:45 -07:00
|
|
|
|
if (RHSRes.isInvalid())
|
|
|
|
|
return ExprError();
|
|
|
|
|
|
|
|
|
|
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,
|
2019-12-06 01:30:21 +02:00
|
|
|
|
std::forward<AtomicEvaluator>(Evaluator));
|
2020-05-12 13:14:32 -07:00
|
|
|
|
}
|
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())
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return ExprError();
|
2019-10-25 00:09:37 +03:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (!SubstitutedAtomicExpr.isUsable())
|
|
|
|
|
// Evaluator has decided satisfaction without yielding an expression.
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return ExprEmpty();
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
2022-09-26 06:57:25 -07:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
EnterExpressionEvaluationContext ConstantEvaluated(
|
|
|
|
|
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
2019-10-28 14:36:31 -07:00
|
|
|
|
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
|
|
|
|
|
Expr::EvalResult EvalResult;
|
|
|
|
|
EvalResult.Diag = &EvaluationDiags;
|
2021-05-19 16:01:45 -07:00
|
|
|
|
if (!SubstitutedAtomicExpr.get()->EvaluateAsConstantExpr(EvalResult,
|
|
|
|
|
S.Context) ||
|
|
|
|
|
!EvaluationDiags.empty()) {
|
|
|
|
|
// C++2a [temp.constr.atomic]p1
|
|
|
|
|
// ...E shall be a constant expression of type bool.
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return ExprError();
|
2019-10-25 00:09:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-19 16:01:45 -07:00
|
|
|
|
assert(EvalResult.Val.isInt() &&
|
|
|
|
|
"evaluating bool expression didn't produce int");
|
2019-12-06 01:30:21 +02:00
|
|
|
|
Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue();
|
|
|
|
|
if (!Satisfaction.IsSatisfied)
|
|
|
|
|
Satisfaction.Details.emplace_back(ConstraintExpr,
|
|
|
|
|
SubstitutedAtomicExpr.get());
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return SubstitutedAtomicExpr;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 08:43:08 -07:00
|
|
|
|
static bool
|
2023-01-27 11:06:07 -08:00
|
|
|
|
DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
|
|
|
|
|
const NamedDecl *Templ, const Expr *E,
|
2022-10-31 08:43:08 -07:00
|
|
|
|
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.
|
2023-01-27 11:06:07 -08:00
|
|
|
|
if (S.SatisfactionStackContains(Templ, ID)) {
|
2022-10-31 08:43:08 -07:00
|
|
|
|
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
|
|
|
|
|
<< const_cast<Expr *>(E) << E->getSourceRange();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
static ExprResult calculateConstraintSatisfaction(
|
2022-07-29 05:52:40 -07:00
|
|
|
|
Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
|
|
|
|
|
const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
|
|
|
|
|
ConstraintSatisfaction &Satisfaction) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
return calculateConstraintSatisfaction(
|
|
|
|
|
S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
|
|
|
|
|
EnterExpressionEvaluationContext ConstantEvaluated(
|
2022-10-24 12:20:36 -07:00
|
|
|
|
S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
|
|
|
|
|
Sema::ReuseLambdaContextDecl);
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
|
|
|
|
// Atomic constraint - substitute arguments and check satisfaction.
|
|
|
|
|
ExprResult SubstitutedExpression;
|
|
|
|
|
{
|
|
|
|
|
TemplateDeductionInfo Info(TemplateNameLoc);
|
|
|
|
|
Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
|
2020-01-25 22:54:27 +02:00
|
|
|
|
Sema::InstantiatingTemplate::ConstraintSubstitution{},
|
|
|
|
|
const_cast<NamedDecl *>(Template), Info,
|
|
|
|
|
AtomicExpr->getSourceRange());
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (Inst.isInvalid())
|
|
|
|
|
return ExprError();
|
2022-10-31 08:43:08 -07:00
|
|
|
|
|
|
|
|
|
llvm::FoldingSetNodeID ID;
|
2023-01-27 11:06:07 -08:00
|
|
|
|
if (Template &&
|
|
|
|
|
DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
|
2022-10-31 08:43:08 -07:00
|
|
|
|
Satisfaction.IsSatisfied = false;
|
|
|
|
|
Satisfaction.ContainsErrors = true;
|
|
|
|
|
return ExprEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 11:06:07 -08:00
|
|
|
|
SatisfactionStackRAII StackRAII(S, Template, ID);
|
2022-10-31 08:43:08 -07:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
// We do not want error diagnostics escaping here.
|
|
|
|
|
Sema::SFINAETrap Trap(S);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
SubstitutedExpression =
|
|
|
|
|
S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
|
2023-01-17 11:29:04 -08:00
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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())
|
2021-09-20 19:42:27 -04:00
|
|
|
|
// A non-SFINAE error has occurred as a result of this
|
2019-12-06 01:30:21 +02:00
|
|
|
|
// 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();
|
|
|
|
|
|
2023-01-17 11:29:04 -08:00
|
|
|
|
// [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());
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
return SubstitutedExpression;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 05:52:40 -07:00
|
|
|
|
static bool CheckConstraintSatisfaction(
|
|
|
|
|
Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
llvm::SmallVectorImpl<Expr *> &Converted,
|
2022-07-29 05:52:40 -07:00
|
|
|
|
const MultiLevelTemplateArgumentList &TemplateArgsLists,
|
|
|
|
|
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (ConstraintExprs.empty()) {
|
|
|
|
|
Satisfaction.IsSatisfied = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 05:52:40 -07:00
|
|
|
|
if (TemplateArgsLists.isAnyArgInstantiationDependent()) {
|
|
|
|
|
// No need to check satisfaction for dependent constraint expressions.
|
|
|
|
|
Satisfaction.IsSatisfied = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-06-30 12:03:42 -07:00
|
|
|
|
|
2022-07-29 05:52:40 -07:00
|
|
|
|
ArrayRef<TemplateArgument> TemplateArgs =
|
|
|
|
|
TemplateArgsLists.getNumSubstitutedLevels() > 0
|
|
|
|
|
? TemplateArgsLists.getOutermost()
|
|
|
|
|
: ArrayRef<TemplateArgument> {};
|
2022-07-01 11:20:16 -07:00
|
|
|
|
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
|
2020-01-25 22:54:27 +02:00
|
|
|
|
Sema::InstantiatingTemplate::ConstraintsCheck{},
|
|
|
|
|
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (Inst.isInvalid())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
for (const Expr *ConstraintExpr : ConstraintExprs) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
ExprResult Res = calculateConstraintSatisfaction(
|
|
|
|
|
S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
|
|
|
|
|
ConstraintExpr, Satisfaction);
|
|
|
|
|
if (Res.isInvalid())
|
2019-12-06 01:30:21 +02:00
|
|
|
|
return true;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
|
|
|
|
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);
|
2019-12-06 01:30:21 +02:00
|
|
|
|
// [temp.constr.op] p2
|
2022-08-19 13:57:45 -07:00
|
|
|
|
// [...] 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. [...]
|
2019-12-06 01:30:21 +02:00
|
|
|
|
return false;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
}
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 02:50:12 +02:00
|
|
|
|
bool Sema::CheckConstraintSatisfaction(
|
2020-01-25 22:54:27 +02:00
|
|
|
|
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
|
2022-07-29 05:52:40 -07:00
|
|
|
|
const MultiLevelTemplateArgumentList &TemplateArgsLists,
|
|
|
|
|
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
|
2020-01-22 02:50:12 +02:00
|
|
|
|
if (ConstraintExprs.empty()) {
|
|
|
|
|
OutSatisfaction.IsSatisfied = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-05-05 15:52:04 +00:00
|
|
|
|
if (!Template) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return ::CheckConstraintSatisfaction(
|
|
|
|
|
*this, nullptr, ConstraintExprs, ConvertedConstraints,
|
|
|
|
|
TemplateArgsLists, TemplateIDRange, OutSatisfaction);
|
2022-05-04 15:31:59 +00:00
|
|
|
|
}
|
2022-07-29 05:52:40 -07:00
|
|
|
|
|
|
|
|
|
// 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;
|
2022-08-14 13:48:18 +02:00
|
|
|
|
for (auto List : TemplateArgsLists)
|
|
|
|
|
FlattenedArgs.insert(FlattenedArgs.end(), List.Args.begin(),
|
|
|
|
|
List.Args.end());
|
2022-07-29 05:52:40 -07:00
|
|
|
|
|
2020-01-22 02:50:12 +02:00
|
|
|
|
llvm::FoldingSetNodeID ID;
|
2022-07-29 05:52:40 -07:00
|
|
|
|
ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
|
2020-01-22 02:50:12 +02:00
|
|
|
|
void *InsertPos;
|
2022-05-04 15:31:59 +00:00
|
|
|
|
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
|
|
|
|
|
OutSatisfaction = *Cached;
|
|
|
|
|
return false;
|
2020-01-22 02:50:12 +02:00
|
|
|
|
}
|
2022-10-28 10:02:04 -07:00
|
|
|
|
|
2022-05-04 15:31:59 +00:00
|
|
|
|
auto Satisfaction =
|
2022-07-29 05:52:40 -07:00
|
|
|
|
std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
|
2020-01-25 22:54:27 +02:00
|
|
|
|
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
ConvertedConstraints, TemplateArgsLists,
|
|
|
|
|
TemplateIDRange, *Satisfaction)) {
|
2022-10-31 08:43:08 -07:00
|
|
|
|
OutSatisfaction = *Satisfaction;
|
2020-01-22 02:50:12 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-10-28 10:02:04 -07:00
|
|
|
|
|
|
|
|
|
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.
|
2022-05-04 15:31:59 +00:00
|
|
|
|
OutSatisfaction = *Satisfaction;
|
|
|
|
|
// We cannot use InsertPos here because CheckConstraintSatisfaction might have
|
|
|
|
|
// invalidated it.
|
2022-05-05 15:02:36 +00:00
|
|
|
|
// Note that entries of SatisfactionCache are deleted in Sema's destructor.
|
2022-05-04 15:31:59 +00:00
|
|
|
|
SatisfactionCache.InsertNode(Satisfaction.release());
|
2020-01-22 02:50:12 +02:00
|
|
|
|
return false;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
|
|
|
|
|
ConstraintSatisfaction &Satisfaction) {
|
|
|
|
|
return calculateConstraintSatisfaction(
|
2022-08-19 13:57:45 -07:00
|
|
|
|
*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();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 22:58:43 +01:00
|
|
|
|
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 (cast<CXXMethodDecl>(Function)->isConst()) {
|
|
|
|
|
QualType T = CapturedVar->getType();
|
|
|
|
|
T.addConst();
|
|
|
|
|
CapturedVar->setType(T);
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
bool Sema::SetupConstraintScope(
|
2023-01-14 12:31:01 -08:00
|
|
|
|
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
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()) {
|
2022-09-25 17:18:39 +02:00
|
|
|
|
MultiLevelTemplateArgumentList JustTemplArgs(FD, SpecArgs->asArray(),
|
|
|
|
|
/*Final=*/false);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
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.
|
|
|
|
|
if (const auto *FromMemTempl =
|
|
|
|
|
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
|
|
|
|
|
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
|
|
|
|
|
Scope, MLTAL))
|
|
|
|
|
return true;
|
2022-02-06 22:58:43 +01:00
|
|
|
|
// Make sure the captures are also added to the instantiation scope.
|
|
|
|
|
if (isLambdaCallOperator(FD) &&
|
|
|
|
|
addInstantiatedCapturesToScope(FD, FromMemTempl->getTemplatedDecl(),
|
|
|
|
|
Scope, MLTAL))
|
|
|
|
|
return true;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
2022-02-06 22:58:43 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Make sure the captures are also added to the instantiation scope.
|
|
|
|
|
if (isLambdaCallOperator(FD) &&
|
|
|
|
|
addInstantiatedCapturesToScope(FD, InstantiatedFrom, Scope, MLTAL))
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function collects all of the template arguments for the purposes of
|
|
|
|
|
// constraint-instantiation and checking.
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<MultiLevelTemplateArgumentList>
|
2022-08-19 13:57:45 -07:00
|
|
|
|
Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
|
2023-01-14 12:31:01 -08:00
|
|
|
|
FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
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.
|
2022-09-25 17:18:39 +02:00
|
|
|
|
MLTAL =
|
|
|
|
|
getTemplateInstantiationArgs(FD, /*Final=*/false, /*Innermost=*/nullptr,
|
|
|
|
|
/*RelativeToPrimary=*/true,
|
|
|
|
|
/*Pattern=*/nullptr,
|
|
|
|
|
/*ForConstraintInstantiation=*/true);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
|
|
|
|
return MLTAL;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-25 22:54:27 +02:00
|
|
|
|
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
|
|
|
|
|
ConstraintSatisfaction &Satisfaction,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
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) {
|
2020-01-30 20:46:32 +02:00
|
|
|
|
Satisfaction.IsSatisfied = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
2022-09-26 11:02:39 -07:00
|
|
|
|
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};
|
2022-08-19 13:57:45 -07:00
|
|
|
|
LocalInstantiationScope Scope(*this, !ForOverloadResolution ||
|
|
|
|
|
isLambdaCallOperator(FD));
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<MultiLevelTemplateArgumentList> MLTAL =
|
2022-08-19 13:57:45 -07:00
|
|
|
|
SetupConstraintCheckingTemplateArgumentsAndScope(
|
|
|
|
|
const_cast<FunctionDecl *>(FD), {}, Scope);
|
|
|
|
|
|
2022-10-21 07:31:26 -07:00
|
|
|
|
if (!MLTAL)
|
|
|
|
|
return true;
|
|
|
|
|
|
2020-02-05 00:51:40 +02:00
|
|
|
|
Qualifiers ThisQuals;
|
|
|
|
|
CXXRecordDecl *Record = nullptr;
|
|
|
|
|
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
|
|
|
|
|
ThisQuals = Method->getMethodQualifiers();
|
|
|
|
|
Record = const_cast<CXXRecordDecl *>(Method->getParent());
|
|
|
|
|
}
|
|
|
|
|
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
|
2023-04-02 01:11:30 -07:00
|
|
|
|
return CheckConstraintSatisfaction(
|
|
|
|
|
FD, {FD->getTrailingRequiresClause()}, *MLTAL,
|
|
|
|
|
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
|
|
|
|
|
Satisfaction);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
2022-10-27 06:27:02 -07:00
|
|
|
|
static unsigned
|
|
|
|
|
CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
|
|
|
|
|
bool SkipForSpecialization = false) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
|
2022-09-25 17:18:39 +02:00
|
|
|
|
ND, /*Final=*/false, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true,
|
2022-09-27 06:35:27 -07:00
|
|
|
|
/*Pattern=*/nullptr,
|
2022-10-27 06:27:02 -07:00
|
|
|
|
/*ForConstraintInstantiation=*/true, SkipForSpecialization);
|
2023-04-27 21:33:32 +00:00
|
|
|
|
return MLTAL.getNumLevels();
|
2022-08-19 13:57:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
|
|
|
|
|
unsigned TemplateDepth = 0;
|
|
|
|
|
public:
|
|
|
|
|
using inherited = TreeTransform<AdjustConstraintDepth>;
|
|
|
|
|
AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
|
|
|
|
|
: inherited(SemaRef), TemplateDepth(TemplateDepth) {}
|
2022-09-25 17:18:39 +02:00
|
|
|
|
|
|
|
|
|
using inherited::TransformTemplateTypeParmType;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
|
2022-09-25 17:18:39 +02:00
|
|
|
|
TemplateTypeParmTypeLoc TL, bool) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
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
|
|
|
|
|
|
2023-04-27 21:33:32 +00:00
|
|
|
|
static const Expr *SubstituteConstraintExpression(Sema &S, const NamedDecl *ND,
|
|
|
|
|
const Expr *ConstrExpr) {
|
|
|
|
|
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
|
|
|
|
|
ND, /*Final=*/false, /*Innermost=*/nullptr,
|
|
|
|
|
/*RelativeToPrimary=*/true,
|
|
|
|
|
/*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
|
|
|
|
|
/*SkipForSpecialization*/ false);
|
|
|
|
|
if (MLTAL.getNumSubstitutedLevels() == 0)
|
|
|
|
|
return ConstrExpr;
|
|
|
|
|
|
|
|
|
|
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
|
|
|
|
|
std::optional<Sema::CXXThisScopeRAII> ThisScope;
|
|
|
|
|
if (auto *RD = dyn_cast<CXXRecordDecl>(ND->getDeclContext()))
|
|
|
|
|
ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
|
|
|
|
|
ExprResult SubstConstr =
|
|
|
|
|
S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
|
|
|
|
|
if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
|
|
|
|
|
return nullptr;
|
|
|
|
|
return SubstConstr.get();
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
|
|
|
|
|
const Expr *OldConstr,
|
|
|
|
|
const NamedDecl *New,
|
|
|
|
|
const Expr *NewConstr) {
|
2023-04-27 21:33:32 +00:00
|
|
|
|
if (OldConstr == NewConstr)
|
|
|
|
|
return true;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
if (Old && New && Old != New) {
|
2023-04-27 21:33:32 +00:00
|
|
|
|
if (const Expr *SubstConstr =
|
|
|
|
|
SubstituteConstraintExpression(*this, Old, OldConstr))
|
|
|
|
|
OldConstr = SubstConstr;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
if (const Expr *SubstConstr =
|
|
|
|
|
SubstituteConstraintExpression(*this, New, NewConstr))
|
|
|
|
|
NewConstr = SubstConstr;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2022-10-07 06:12:03 -07:00
|
|
|
|
if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth,
|
2022-08-19 13:57:45 -07:00
|
|
|
|
Constraint))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
2020-01-25 22:54:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
bool Sema::EnsureTemplateArgumentListConstraints(
|
2022-07-29 05:52:40 -07:00
|
|
|
|
TemplateDecl *TD, const MultiLevelTemplateArgumentList &TemplateArgsLists,
|
2019-12-06 01:30:21 +02:00
|
|
|
|
SourceRange TemplateIDRange) {
|
|
|
|
|
ConstraintSatisfaction Satisfaction;
|
|
|
|
|
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
|
|
|
|
|
TD->getAssociatedConstraints(AssociatedConstraints);
|
2022-07-29 05:52:40 -07:00
|
|
|
|
if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgsLists,
|
2019-12-06 01:30:21 +02:00
|
|
|
|
TemplateIDRange, Satisfaction))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!Satisfaction.IsSatisfied) {
|
|
|
|
|
SmallString<128> TemplateArgString;
|
|
|
|
|
TemplateArgString = " ";
|
|
|
|
|
TemplateArgString += getTemplateArgumentBindingsText(
|
2022-07-29 05:52:40 -07:00
|
|
|
|
TD->getTemplateParameters(), TemplateArgsLists.getInnermost().data(),
|
|
|
|
|
TemplateArgsLists.getInnermost().size());
|
2019-12-06 01:30:21 +02:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 11:45:44 -08:00
|
|
|
|
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);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<MultiLevelTemplateArgumentList> MLTAL =
|
2022-08-19 13:57:45 -07:00
|
|
|
|
SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
|
|
|
|
|
Scope);
|
|
|
|
|
|
|
|
|
|
if (!MLTAL)
|
|
|
|
|
return true;
|
|
|
|
|
|
2022-03-03 11:45:44 -08:00
|
|
|
|
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);
|
2022-09-27 06:35:27 -07:00
|
|
|
|
FunctionScopeRAII FuncScope(*this);
|
|
|
|
|
if (isLambdaCallOperator(Decl))
|
|
|
|
|
PushLambdaScope();
|
|
|
|
|
else
|
|
|
|
|
FuncScope.disable();
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
llvm::SmallVector<Expr *, 1> Converted;
|
|
|
|
|
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
|
2022-03-03 11:45:44 -08:00
|
|
|
|
PointOfInstantiation, Satisfaction);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-18 09:11:43 +02:00
|
|
|
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
|
|
|
|
concepts::ExprRequirement *Req,
|
|
|
|
|
bool First) {
|
|
|
|
|
assert(!Req->isSatisfied()
|
|
|
|
|
&& "Diagnose() can only be used on an unsatisfied requirement");
|
|
|
|
|
switch (Req->getSatisfactionStatus()) {
|
|
|
|
|
case concepts::ExprRequirement::SS_Dependent:
|
|
|
|
|
llvm_unreachable("Diagnosing a dependent requirement");
|
|
|
|
|
break;
|
|
|
|
|
case concepts::ExprRequirement::SS_ExprSubstitutionFailure: {
|
|
|
|
|
auto *SubstDiag = Req->getExprSubstitutionDiagnostic();
|
|
|
|
|
if (!SubstDiag->DiagMessage.empty())
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_expr_requirement_expr_substitution_error)
|
|
|
|
|
<< (int)First << SubstDiag->SubstitutedEntity
|
|
|
|
|
<< SubstDiag->DiagMessage;
|
|
|
|
|
else
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_expr_requirement_expr_unknown_substitution_error)
|
|
|
|
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case concepts::ExprRequirement::SS_NoexceptNotMet:
|
|
|
|
|
S.Diag(Req->getNoexceptLoc(),
|
|
|
|
|
diag::note_expr_requirement_noexcept_not_met)
|
|
|
|
|
<< (int)First << Req->getExpr();
|
|
|
|
|
break;
|
|
|
|
|
case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: {
|
|
|
|
|
auto *SubstDiag =
|
|
|
|
|
Req->getReturnTypeRequirement().getSubstitutionDiagnostic();
|
|
|
|
|
if (!SubstDiag->DiagMessage.empty())
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_expr_requirement_type_requirement_substitution_error)
|
|
|
|
|
<< (int)First << SubstDiag->SubstitutedEntity
|
|
|
|
|
<< SubstDiag->DiagMessage;
|
|
|
|
|
else
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_expr_requirement_type_requirement_unknown_substitution_error)
|
|
|
|
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
|
|
|
|
|
ConceptSpecializationExpr *ConstraintExpr =
|
|
|
|
|
Req->getReturnTypeRequirementSubstitutedConstraintExpr();
|
2021-03-08 01:09:13 +01:00
|
|
|
|
if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
|
2020-01-18 09:11:43 +02:00
|
|
|
|
// A simple case - expr type is the type being constrained and the concept
|
|
|
|
|
// was not provided arguments.
|
2021-03-08 01:09:13 +01:00
|
|
|
|
Expr *e = Req->getExpr();
|
|
|
|
|
S.Diag(e->getBeginLoc(),
|
2020-01-18 09:11:43 +02:00
|
|
|
|
diag::note_expr_requirement_constraints_not_satisfied_simple)
|
2021-04-17 23:50:27 +02:00
|
|
|
|
<< (int)First << S.Context.getReferenceQualifiedType(e)
|
2020-01-18 09:11:43 +02:00
|
|
|
|
<< ConstraintExpr->getNamedConcept();
|
2021-03-08 01:09:13 +01:00
|
|
|
|
} else {
|
2020-01-18 09:11:43 +02:00
|
|
|
|
S.Diag(ConstraintExpr->getBeginLoc(),
|
|
|
|
|
diag::note_expr_requirement_constraints_not_satisfied)
|
|
|
|
|
<< (int)First << ConstraintExpr;
|
2021-03-08 01:09:13 +01:00
|
|
|
|
}
|
2020-01-18 09:11:43 +02:00
|
|
|
|
S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case concepts::ExprRequirement::SS_Satisfied:
|
|
|
|
|
llvm_unreachable("We checked this above");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
|
|
|
|
concepts::TypeRequirement *Req,
|
|
|
|
|
bool First) {
|
|
|
|
|
assert(!Req->isSatisfied()
|
|
|
|
|
&& "Diagnose() can only be used on an unsatisfied requirement");
|
|
|
|
|
switch (Req->getSatisfactionStatus()) {
|
|
|
|
|
case concepts::TypeRequirement::SS_Dependent:
|
|
|
|
|
llvm_unreachable("Diagnosing a dependent requirement");
|
|
|
|
|
return;
|
|
|
|
|
case concepts::TypeRequirement::SS_SubstitutionFailure: {
|
|
|
|
|
auto *SubstDiag = Req->getSubstitutionDiagnostic();
|
|
|
|
|
if (!SubstDiag->DiagMessage.empty())
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_type_requirement_substitution_error) << (int)First
|
|
|
|
|
<< SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage;
|
|
|
|
|
else
|
|
|
|
|
S.Diag(SubstDiag->DiagLoc,
|
|
|
|
|
diag::note_type_requirement_unknown_substitution_error)
|
|
|
|
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
llvm_unreachable("Unknown satisfaction status");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-29 14:05:53 +01:00
|
|
|
|
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
|
|
|
|
|
Expr *SubstExpr,
|
|
|
|
|
bool First = true);
|
2020-01-18 09:11:43 +02:00
|
|
|
|
|
|
|
|
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
|
|
|
|
concepts::NestedRequirement *Req,
|
|
|
|
|
bool First) {
|
2022-11-29 14:05:53 +01:00
|
|
|
|
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;
|
2020-01-18 09:11:43 +02:00
|
|
|
|
else
|
2022-11-29 14:05:53 +01:00
|
|
|
|
diagnoseWellFormedUnsatisfiedConstraintExpr(
|
|
|
|
|
S, Pair.second.dyn_cast<Expr *>(), First);
|
|
|
|
|
First = false;
|
2020-01-18 09:11:43 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 01:30:21 +02:00
|
|
|
|
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
|
|
|
|
|
Expr *SubstExpr,
|
2022-11-29 14:05:53 +01:00
|
|
|
|
bool First) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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;
|
2021-05-19 16:01:45 -07:00
|
|
|
|
case BO_LAnd: {
|
|
|
|
|
bool LHSSatisfied =
|
|
|
|
|
BO->getLHS()->EvaluateKnownConstInt(S.Context).getBoolValue();
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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
|
2021-05-19 16:01:45 -07:00
|
|
|
|
bool RHSSatisfied =
|
|
|
|
|
BO->getRHS()->EvaluateKnownConstInt(S.Context).getBoolValue();
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (!RHSSatisfied)
|
|
|
|
|
diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(),
|
|
|
|
|
/*First=*/false);
|
|
|
|
|
return;
|
2021-05-19 16:01:45 -07:00
|
|
|
|
}
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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;
|
2021-05-19 16:01:45 -07:00
|
|
|
|
BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context,
|
|
|
|
|
Expr::SE_NoSideEffects,
|
|
|
|
|
/*InConstantContext=*/true);
|
|
|
|
|
BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context,
|
|
|
|
|
Expr::SE_NoSideEffects,
|
|
|
|
|
/*InConstantContext=*/true);
|
2019-12-06 01:30:21 +02:00
|
|
|
|
if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
|
|
|
|
|
S.Diag(SubstExpr->getBeginLoc(),
|
|
|
|
|
diag::note_atomic_constraint_evaluated_to_false_elaborated)
|
|
|
|
|
<< (int)First << SubstExpr
|
2021-06-11 13:19:00 +01:00
|
|
|
|
<< toString(SimplifiedLHS.Val.getInt(), 10)
|
2019-12-06 01:30:21 +02:00
|
|
|
|
<< BinaryOperator::getOpcodeStr(BO->getOpcode())
|
2021-06-11 13:19:00 +01:00
|
|
|
|
<< toString(SimplifiedRHS.Val.getInt(), 10);
|
2019-12-06 01:30:21 +02:00
|
|
|
|
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;
|
2020-01-18 09:11:43 +02:00
|
|
|
|
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
|
2022-12-22 05:05:31 +01:00
|
|
|
|
// FIXME: RequiresExpr should store dependent diagnostics.
|
2020-01-18 09:11:43 +02:00
|
|
|
|
for (concepts::Requirement *Req : RE->getRequirements())
|
|
|
|
|
if (!Req->isDependent() && !Req->isSatisfied()) {
|
|
|
|
|
if (auto *E = dyn_cast<concepts::ExprRequirement>(Req))
|
|
|
|
|
diagnoseUnsatisfiedRequirement(S, E, First);
|
|
|
|
|
else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req))
|
|
|
|
|
diagnoseUnsatisfiedRequirement(S, T, First);
|
|
|
|
|
else
|
|
|
|
|
diagnoseUnsatisfiedRequirement(
|
|
|
|
|
S, cast<concepts::NestedRequirement>(Req), First);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return;
|
2019-12-06 01:30:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 09:11:43 +02:00
|
|
|
|
void
|
|
|
|
|
Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
|
|
|
|
|
bool First) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
assert(!Satisfaction.IsSatisfied &&
|
|
|
|
|
"Attempted to diagnose a satisfied constraint");
|
|
|
|
|
for (auto &Pair : Satisfaction.Details) {
|
|
|
|
|
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
|
|
|
|
First = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Sema::DiagnoseUnsatisfiedConstraint(
|
2020-01-18 09:11:43 +02:00
|
|
|
|
const ASTConstraintSatisfaction &Satisfaction,
|
|
|
|
|
bool First) {
|
2019-12-06 01:30:21 +02:00
|
|
|
|
assert(!Satisfaction.IsSatisfied &&
|
|
|
|
|
"Attempted to diagnose a satisfied constraint");
|
|
|
|
|
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
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
static bool
|
|
|
|
|
substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
|
|
|
|
ConceptDecl *Concept,
|
|
|
|
|
const MultiLevelTemplateArgumentList &MLTAL,
|
|
|
|
|
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
|
2019-12-23 08:37:35 +02:00
|
|
|
|
if (!N.isAtomic()) {
|
2022-08-19 13:57:45 -07:00
|
|
|
|
if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
|
2019-12-23 08:37:35 +02:00
|
|
|
|
ArgsAsWritten))
|
|
|
|
|
return true;
|
2022-08-19 13:57:45 -07:00
|
|
|
|
return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
|
2019-12-23 08:37:35 +02:00
|
|
|
|
ArgsAsWritten);
|
|
|
|
|
}
|
|
|
|
|
TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
|
|
|
|
|
|
|
|
|
|
AtomicConstraint &Atomic = *N.getAtomicConstraint();
|
|
|
|
|
TemplateArgumentListInfo SubstArgs;
|
|
|
|
|
if (!Atomic.ParameterMapping) {
|
|
|
|
|
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
|
|
|
|
|
S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
|
|
|
|
|
/*Depth=*/0, OccurringIndices);
|
2022-08-10 10:47:17 -07:00
|
|
|
|
TemplateArgumentLoc *TempArgs =
|
|
|
|
|
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
|
2020-01-09 15:07:51 +02:00
|
|
|
|
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
|
2019-12-23 08:37:35 +02:00
|
|
|
|
if (OccurringIndices[I])
|
2022-08-10 10:47:17 -07:00
|
|
|
|
new (&(TempArgs)[J++])
|
|
|
|
|
TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc(
|
|
|
|
|
TemplateParams->begin()[I],
|
2019-12-23 08:37:35 +02:00
|
|
|
|
// 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.
|
2022-08-10 10:47:17 -07:00
|
|
|
|
ArgsAsWritten->NumTemplateArgs > I
|
|
|
|
|
? ArgsAsWritten->arguments()[I].getLocation()
|
|
|
|
|
: SourceLocation()));
|
|
|
|
|
Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count());
|
2019-12-23 08:37:35 +02:00
|
|
|
|
}
|
|
|
|
|
Sema::InstantiatingTemplate Inst(
|
|
|
|
|
S, ArgsAsWritten->arguments().front().getSourceRange().getBegin(),
|
|
|
|
|
Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, Concept,
|
2023-01-26 11:11:29 -08:00
|
|
|
|
ArgsAsWritten->arguments().front().getSourceRange());
|
2022-07-01 11:20:16 -07:00
|
|
|
|
if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
|
2019-12-23 08:37:35 +02:00
|
|
|
|
return true;
|
2022-08-10 10:47:17 -07:00
|
|
|
|
|
|
|
|
|
TemplateArgumentLoc *TempArgs =
|
|
|
|
|
new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
|
2019-12-23 08:37:35 +02:00
|
|
|
|
std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
|
2022-08-10 10:47:17 -07:00
|
|
|
|
TempArgs);
|
|
|
|
|
Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
|
2019-12-23 08:37:35 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
|
|
|
|
const ConceptSpecializationExpr *CSE) {
|
|
|
|
|
TemplateArgumentList TAL{TemplateArgumentList::OnStack,
|
|
|
|
|
CSE->getTemplateArguments()};
|
2022-09-25 17:18:39 +02:00
|
|
|
|
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
|
2023-01-17 06:12:40 -08:00
|
|
|
|
CSE->getNamedConcept(), /*Final=*/false, &TAL,
|
2022-09-25 17:18:39 +02:00
|
|
|
|
/*RelativeToPrimary=*/true,
|
|
|
|
|
/*Pattern=*/nullptr,
|
|
|
|
|
/*ForConstraintInstantiation=*/true);
|
2022-08-19 13:57:45 -07:00
|
|
|
|
|
|
|
|
|
return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
|
|
|
|
|
CSE->getTemplateArgsAsWritten());
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<NormalizedConstraint>
|
2020-01-09 15:07:51 +02:00
|
|
|
|
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
|
|
|
|
|
ArrayRef<const Expr *> E) {
|
|
|
|
|
assert(E.size() != 0);
|
2021-07-26 01:42:14 +02:00
|
|
|
|
auto Conjunction = fromConstraintExpr(S, D, E[0]);
|
|
|
|
|
if (!Conjunction)
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2021-07-26 01:42:14 +02:00
|
|
|
|
for (unsigned I = 1; I < E.size(); ++I) {
|
2020-01-09 15:07:51 +02:00
|
|
|
|
auto Next = fromConstraintExpr(S, D, E[I]);
|
|
|
|
|
if (!Next)
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2021-07-26 01:42:14 +02:00
|
|
|
|
*Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction),
|
2020-01-09 15:07:51 +02:00
|
|
|
|
std::move(*Next), CCK_Conjunction);
|
|
|
|
|
}
|
|
|
|
|
return Conjunction;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<NormalizedConstraint>
|
2019-12-23 08:37:35 +02:00
|
|
|
|
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();
|
[C++20] Implement P2113R0: Changes to the Partial Ordering of Constrained Functions
This implementation matches GCC behavior in that [[ https://eel.is/c++draft/temp.func.order#6.2.1 | temp.func.order p6.2.1 ]] is not implemented [1]. I reached out to the GCC author to confirm that some changes elsewhere to overload resolution are probably needed, but no solution has been developed sufficiently [3].
Most of the wordings are implemented straightforwardly. However,
for [[ https://eel.is/c++draft/temp.func.order#6.2.2 | temp.func.order p6.2.2 ]] "... or if the function parameters that positionally correspond between the two templates are not of the same type", the "same type" is not very clear ([2] is a bug related to this). Here is a quick example
```
template <C T, C U> int f(T, U);
template <typename T, C U> int f(U, T);
int x = f(0, 0);
```
Is the `U` and `T` from different `f`s the "same type"? The answer is NO even though both `U` and `T` are deduced to be `int` in this case. The reason is that `U` and `T` are dependent types, according to [[ https://eel.is/c++draft/temp.over.link#3 | temp.over.link p3 ]], they can not be the "same type".
To check if two function parameters are the "same type":
* For //function template//: compare the function parameter canonical types and return type between two function templates.
* For //class template/partial specialization//: by [[ https://eel.is/c++draft/temp.spec.partial.order#1.2 | temp.spec.partial.order p1.2 ]], compare the injected template arguments between two templates using hashing(TemplateArgument::Profile) is enough.
[1] https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=57b4daf8dc4ed7b669cc70638866ddb00f5b7746
[2] https://github.com/llvm/llvm-project/issues/49308
[3] https://lists.isocpp.org/core/2020/06/index.php#msg9392
Fixes https://github.com/llvm/llvm-project/issues/54039
Fixes https://github.com/llvm/llvm-project/issues/49308 (PR49964)
Reviewed By: royjacobson, #clang-language-wg, mizvekov
Differential Revision: https://reviews.llvm.org/D128750
2022-10-18 11:51:02 -07:00
|
|
|
|
|
|
|
|
|
// C++2a [temp.param]p4:
|
|
|
|
|
// [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
|
2022-10-22 20:17:06 -07:00
|
|
|
|
// Fold expression is considered atomic constraints per current wording.
|
|
|
|
|
// See http://cplusplus.github.io/concepts-ts/ts-active.html#28
|
[C++20] Implement P2113R0: Changes to the Partial Ordering of Constrained Functions
This implementation matches GCC behavior in that [[ https://eel.is/c++draft/temp.func.order#6.2.1 | temp.func.order p6.2.1 ]] is not implemented [1]. I reached out to the GCC author to confirm that some changes elsewhere to overload resolution are probably needed, but no solution has been developed sufficiently [3].
Most of the wordings are implemented straightforwardly. However,
for [[ https://eel.is/c++draft/temp.func.order#6.2.2 | temp.func.order p6.2.2 ]] "... or if the function parameters that positionally correspond between the two templates are not of the same type", the "same type" is not very clear ([2] is a bug related to this). Here is a quick example
```
template <C T, C U> int f(T, U);
template <typename T, C U> int f(U, T);
int x = f(0, 0);
```
Is the `U` and `T` from different `f`s the "same type"? The answer is NO even though both `U` and `T` are deduced to be `int` in this case. The reason is that `U` and `T` are dependent types, according to [[ https://eel.is/c++draft/temp.over.link#3 | temp.over.link p3 ]], they can not be the "same type".
To check if two function parameters are the "same type":
* For //function template//: compare the function parameter canonical types and return type between two function templates.
* For //class template/partial specialization//: by [[ https://eel.is/c++draft/temp.spec.partial.order#1.2 | temp.spec.partial.order p1.2 ]], compare the injected template arguments between two templates using hashing(TemplateArgument::Profile) is enough.
[1] https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=57b4daf8dc4ed7b669cc70638866ddb00f5b7746
[2] https://github.com/llvm/llvm-project/issues/49308
[3] https://lists.isocpp.org/core/2020/06/index.php#msg9392
Fixes https://github.com/llvm/llvm-project/issues/54039
Fixes https://github.com/llvm/llvm-project/issues/49308 (PR49964)
Reviewed By: royjacobson, #clang-language-wg, mizvekov
Differential Revision: https://reviews.llvm.org/D128750
2022-10-18 11:51:02 -07:00
|
|
|
|
|
2020-05-12 13:14:32 -07:00
|
|
|
|
if (LogicalBinOp BO = E) {
|
|
|
|
|
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
|
|
|
|
|
if (!LHS)
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2020-05-12 13:14:32 -07:00
|
|
|
|
auto RHS = fromConstraintExpr(S, D, BO.getRHS());
|
|
|
|
|
if (!RHS)
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2020-05-12 13:14:32 -07:00
|
|
|
|
|
|
|
|
|
return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
|
|
|
|
|
BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
|
2019-12-23 08:37:35 +02:00
|
|
|
|
} 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)
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2019-12-23 08:37:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-14 12:31:01 -08:00
|
|
|
|
std::optional<NormalizedConstraint> New;
|
2020-01-09 15:07:51 +02:00
|
|
|
|
New.emplace(S.Context, *SubNF);
|
|
|
|
|
|
2022-08-19 13:57:45 -07:00
|
|
|
|
if (substituteParameterMappings(S, *New, CSE))
|
2022-12-03 11:13:39 -08:00
|
|
|
|
return std::nullopt;
|
2019-12-23 08:37:35 +02:00
|
|
|
|
|
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>
|
[NFC][clang] Fix static analyzer tool remarks about large copies by values
Reported by Coverity:
Big parameter passed by value
Copying large values is inefficient, consider passing by reference; Low, medium, and high size thresholds for detection can be adjusted.
1. Inside "SemaConcept.cpp" file, in subsumes<clang::Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(clang::NamedDecl *, llvm::ArrayRef<clang::Expr const *>, clang::NamedDecl *, llvm::ArrayRef<clang::Expr const *>)::[lambda(clang::AtomicConstraint const &, clang::AtomicConstraint const &) (instance 2)]>(llvm::SmallVector<llvm::SmallVector<clang::AtomicConstraint *, 2u>, 4u>, llvm::SmallVector<llvm::SmallVector<clang::AtomicConstraint *, 2u>, 4u>, T1): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter PDNF of type NormalForm (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
ii. pass_by_value: Passing parameter QCNF of type NormalForm (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
2. Inside "CodeGenAction.cpp" file, in clang::reportOptRecordError(llvm::Error, clang::DiagnosticsEngine &, clang::CodeGenOptions): A very large function call parameter exceeding the high threshold is passed by value.
i. pass_by_value: Passing parameter CodeGenOpts of type clang::CodeGenOptions const (size 1560 bytes) by value, which exceeds the high threshold of 512 bytes.
3. Inside "SemaCodeComplete.cpp" file, in HandleCodeCompleteResults(clang::Sema *, clang::CodeCompleteConsumer *, clang::CodeCompletionContext, clang::CodeCompletionResult *, unsigned int): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter Context of type clang::CodeCompletionContext (size 200 bytes) by value, which exceeds the low threshold of 128 bytes.
4. Inside "SemaConcept.cpp" file, in <unnamed>::SatisfactionStackRAII::SatisfactionStackRAII(clang::Sema &, clang::NamedDecl const *, llvm::FoldingSetNodeID): A large function call parameter exceeding the low threshold is passed by value.
i. pass_by_value: Passing parameter FSNID of type llvm::FoldingSetNodeID (size 144 bytes) by value, which exceeds the low threshold of 128 bytes.
Reviewed By: erichkeane, aaron.ballman
Differential Revision: https://reviews.llvm.org/D147708
2023-04-07 16:37:56 -04:00
|
|
|
|
static bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
|
2020-01-09 15:07:51 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 06:27:02 -07:00
|
|
|
|
bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
|
|
|
|
|
MutableArrayRef<const Expr *> AC1,
|
|
|
|
|
NamedDecl *D2,
|
|
|
|
|
MutableArrayRef<const Expr *> AC2,
|
2019-12-23 08:37:35 +02:00
|
|
|
|
bool &Result) {
|
2022-10-30 21:33:22 -07:00
|
|
|
|
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);
|
2022-10-31 08:53:04 +01:00
|
|
|
|
(void)IsExpectedEntity;
|
2023-01-12 12:55:21 +01:00
|
|
|
|
(void)FD1;
|
2022-10-31 08:53:04 +01:00
|
|
|
|
(void)FD2;
|
2022-10-30 21:33:22 -07:00
|
|
|
|
assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
|
|
|
|
|
"use non-instantiated function declaration for constraints partial "
|
|
|
|
|
"ordering");
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-23 08:37:35 +02:00
|
|
|
|
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
|
|
|
|
|
2022-10-27 06:27:02 -07:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2021-09-20 19:42:27 -04:00
|
|
|
|
EA->Profile(IDA, Context, /*Canonical=*/true);
|
|
|
|
|
EB->Profile(IDB, Context, /*Canonical=*/true);
|
2020-01-09 15:07:51 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2020-01-18 09:11:43 +02:00
|
|
|
|
|
|
|
|
|
concepts::ExprRequirement::ExprRequirement(
|
|
|
|
|
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
|
|
|
|
|
ReturnTypeRequirement Req, SatisfactionStatus Status,
|
|
|
|
|
ConceptSpecializationExpr *SubstitutedConstraintExpr) :
|
|
|
|
|
Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent,
|
|
|
|
|
Status == SS_Dependent &&
|
|
|
|
|
(E->containsUnexpandedParameterPack() ||
|
|
|
|
|
Req.containsUnexpandedParameterPack()),
|
|
|
|
|
Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc),
|
|
|
|
|
TypeReq(Req), SubstitutedConstraintExpr(SubstitutedConstraintExpr),
|
|
|
|
|
Status(Status) {
|
|
|
|
|
assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) &&
|
|
|
|
|
"Simple requirement must not have a return type requirement or a "
|
|
|
|
|
"noexcept specification");
|
|
|
|
|
assert((Status > SS_TypeRequirementSubstitutionFailure && Req.isTypeConstraint()) ==
|
|
|
|
|
(SubstitutedConstraintExpr != nullptr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
concepts::ExprRequirement::ExprRequirement(
|
|
|
|
|
SubstitutionDiagnostic *ExprSubstDiag, bool IsSimple,
|
|
|
|
|
SourceLocation NoexceptLoc, ReturnTypeRequirement Req) :
|
|
|
|
|
Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(),
|
|
|
|
|
Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false),
|
|
|
|
|
Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req),
|
|
|
|
|
Status(SS_ExprSubstitutionFailure) {
|
|
|
|
|
assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) &&
|
|
|
|
|
"Simple requirement must not have a return type requirement or a "
|
|
|
|
|
"noexcept specification");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
concepts::ExprRequirement::ReturnTypeRequirement::
|
|
|
|
|
ReturnTypeRequirement(TemplateParameterList *TPL) :
|
2022-01-09 00:19:47 -08:00
|
|
|
|
TypeConstraintInfo(TPL, false) {
|
2020-01-18 09:11:43 +02:00
|
|
|
|
assert(TPL->size() == 1);
|
|
|
|
|
const TypeConstraint *TC =
|
|
|
|
|
cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint();
|
2021-08-26 12:39:12 -04:00
|
|
|
|
assert(TC &&
|
|
|
|
|
"TPL must have a template type parameter with a type constraint");
|
2020-01-18 09:11:43 +02:00
|
|
|
|
auto *Constraint =
|
2021-08-24 07:08:18 -04:00
|
|
|
|
cast<ConceptSpecializationExpr>(TC->getImmediatelyDeclaredConstraint());
|
2020-07-15 19:38:46 -07:00
|
|
|
|
bool Dependent =
|
|
|
|
|
Constraint->getTemplateArgsAsWritten() &&
|
|
|
|
|
TemplateSpecializationType::anyInstantiationDependentTemplateArguments(
|
|
|
|
|
Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1));
|
2022-01-09 00:19:47 -08:00
|
|
|
|
TypeConstraintInfo.setInt(Dependent ? true : false);
|
2020-01-18 09:11:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) :
|
2020-07-15 19:38:46 -07:00
|
|
|
|
Requirement(RK_Type, T->getType()->isInstantiationDependentType(),
|
2020-01-18 09:11:43 +02:00
|
|
|
|
T->getType()->containsUnexpandedParameterPack(),
|
|
|
|
|
// We reach this ctor with either dependent types (in which
|
|
|
|
|
// IsSatisfied doesn't matter) or with non-dependent type in
|
|
|
|
|
// which the existence of the type indicates satisfaction.
|
2020-07-15 19:38:46 -07:00
|
|
|
|
/*IsSatisfied=*/true),
|
|
|
|
|
Value(T),
|
|
|
|
|
Status(T->getType()->isInstantiationDependentType() ? SS_Dependent
|
|
|
|
|
: SS_Satisfied) {}
|