aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTConcept.cpp31
-rw-r--r--clang/lib/AST/ASTImporter.cpp12
-rw-r--r--clang/lib/AST/ByteCode/InterpBuiltin.cpp137
-rw-r--r--clang/lib/AST/ExprConstant.cpp148
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp30
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h39
-rw-r--r--clang/lib/CrossTU/CrossTranslationUnit.cpp4
-rw-r--r--clang/lib/Headers/avx10_2bf16intrin.h36
-rw-r--r--clang/lib/Sema/SemaConcept.cpp2000
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp16
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp16
-rw-r--r--clang/lib/Sema/SemaInit.cpp5
-rw-r--r--clang/lib/Sema/SemaOpenACC.cpp11
-rw-r--r--clang/lib/Sema/SemaOverload.cpp6
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp93
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp51
-rw-r--r--clang/lib/Sema/SemaTemplateDeductionGuide.cpp39
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp168
-rw-r--r--clang/lib/Sema/TreeTransform.h19
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp2
-rw-r--r--clang/lib/Serialization/ASTReaderStmt.cpp14
-rw-r--r--clang/lib/Serialization/ASTWriterStmt.cpp18
-rw-r--r--clang/lib/StaticAnalyzer/Core/CMakeLists.txt1
-rw-r--r--clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp31
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp27
25 files changed, 2030 insertions, 924 deletions
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index d658890..fd12bc4 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -24,13 +24,18 @@ static void
CreateUnsatisfiedConstraintRecord(const ASTContext &C,
const UnsatisfiedConstraintRecord &Detail,
UnsatisfiedConstraintRecord *TrailingObject) {
- if (auto *E = dyn_cast<Expr *>(Detail))
+ if (Detail.isNull())
+ new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
+ else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
new (TrailingObject) UnsatisfiedConstraintRecord(E);
+ else if (const auto *Concept =
+ llvm::dyn_cast<const ConceptReference *>(Detail))
+ new (TrailingObject) UnsatisfiedConstraintRecord(Concept);
else {
auto &SubstitutionDiagnostic =
- *cast<std::pair<SourceLocation, StringRef> *>(Detail);
+ *cast<const clang::ConstraintSubstitutionDiagnostic *>(Detail);
StringRef Message = C.backupStr(SubstitutionDiagnostic.second);
- auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>(
+ auto *NewSubstDiag = new (C) clang::ConstraintSubstitutionDiagnostic(
SubstitutionDiagnostic.first, Message);
new (TrailingObject) UnsatisfiedConstraintRecord(NewSubstDiag);
}
@@ -74,9 +79,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild(
return new (Mem) ASTConstraintSatisfaction(C, Satisfaction);
}
-void ConstraintSatisfaction::Profile(
- llvm::FoldingSetNodeID &ID, const ASTContext &C,
- const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
+void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &C,
+ const NamedDecl *ConstraintOwner,
+ ArrayRef<TemplateArgument> TemplateArgs) {
ID.AddPointer(ConstraintOwner);
ID.AddInteger(TemplateArgs.size());
for (auto &Arg : TemplateArgs)
@@ -116,6 +122,19 @@ void ConceptReference::print(llvm::raw_ostream &OS,
}
}
+const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
+ const ConceptReference *C) {
+ std::string NameStr;
+ llvm::raw_string_ostream OS(NameStr);
+ LangOptions LO;
+ LO.CPlusPlus = true;
+ LO.Bool = true;
+ OS << '\'';
+ C->print(OS, PrintingPolicy(LO));
+ OS << '\'';
+ return DB << NameStr;
+}
+
concepts::ExprRequirement::ExprRequirement(
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
ReturnTypeRequirement Req, SatisfactionStatus Status,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 1c8fd83..f43fa8c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1069,22 +1069,22 @@ Error ASTNodeImporter::ImportConstraintSatisfaction(
ToSat.ContainsErrors = FromSat.ContainsErrors;
if (!ToSat.IsSatisfied) {
for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) {
- if (Expr *E = Record->dyn_cast<Expr *>()) {
+ if (const Expr *E = Record->dyn_cast<const Expr *>()) {
ExpectedExpr ToSecondExpr = import(E);
if (!ToSecondExpr)
return ToSecondExpr.takeError();
ToSat.Details.emplace_back(ToSecondExpr.get());
} else {
- auto Pair = Record->dyn_cast<std::pair<SourceLocation, StringRef> *>();
+ auto Pair =
+ Record->dyn_cast<const ConstraintSubstitutionDiagnostic *>();
ExpectedSLoc ToPairFirst = import(Pair->first);
if (!ToPairFirst)
return ToPairFirst.takeError();
StringRef ToPairSecond = ImportASTStringRef(Pair->second);
- ToSat.Details.emplace_back(
- new (Importer.getToContext())
- ConstraintSatisfaction::SubstitutionDiagnostic{
- ToPairFirst.get(), ToPairSecond});
+ ToSat.Details.emplace_back(new (Importer.getToContext())
+ ConstraintSubstitutionDiagnostic{
+ ToPairFirst.get(), ToPairSecond});
}
}
}
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a2e97fc..6053237 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2773,6 +2773,50 @@ static bool interp__builtin_blend(InterpState &S, CodePtr OpPC,
return true;
}
+static bool interp__builtin_ia32_pshuf(InterpState &S, CodePtr OpPC,
+ const CallExpr *Call, bool IsShufHW) {
+ assert(Call->getNumArgs() == 2 && "masked forms handled via select*");
+ APSInt ControlImm = popToAPSInt(S, Call->getArg(1));
+ const Pointer &Src = S.Stk.pop<Pointer>();
+ const Pointer &Dst = S.Stk.peek<Pointer>();
+
+ unsigned NumElems = Dst.getNumElems();
+ PrimType ElemT = Dst.getFieldDesc()->getPrimType();
+
+ unsigned ElemBits = static_cast<unsigned>(primSize(ElemT) * 8);
+ if (ElemBits != 16 && ElemBits != 32)
+ return false;
+
+ unsigned LaneElts = 128u / ElemBits;
+ assert(LaneElts && (NumElems % LaneElts == 0));
+
+ uint8_t Ctl = static_cast<uint8_t>(ControlImm.getZExtValue());
+
+ for (unsigned Idx = 0; Idx != NumElems; Idx++) {
+ unsigned LaneBase = (Idx / LaneElts) * LaneElts;
+ unsigned LaneIdx = Idx % LaneElts;
+ unsigned SrcIdx = Idx;
+ unsigned Sel = (Ctl >> (2 * LaneIdx)) & 0x3;
+ if (ElemBits == 32) {
+ SrcIdx = LaneBase + Sel;
+ } else {
+ constexpr unsigned HalfSize = 4;
+ bool InHigh = LaneIdx >= HalfSize;
+ if (!IsShufHW && !InHigh) {
+ SrcIdx = LaneBase + Sel;
+ } else if (IsShufHW && InHigh) {
+ unsigned Rel = LaneIdx - HalfSize;
+ Sel = (Ctl >> (2 * Rel)) & 0x3;
+ SrcIdx = LaneBase + HalfSize + Sel;
+ }
+ }
+
+ INT_TYPE_SWITCH_NO_BOOL(ElemT, { Dst.elem<T>(Idx) = Src.elem<T>(SrcIdx); });
+ }
+ Dst.initializeAllElements();
+ return true;
+}
+
static bool interp__builtin_elementwise_triop(
InterpState &S, CodePtr OpPC, const CallExpr *Call,
llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &)>
@@ -2878,6 +2922,61 @@ static bool interp__builtin_x86_insert_subvector(InterpState &S, CodePtr OpPC,
return true;
}
+static bool interp__builtin_vec_ext(InterpState &S, CodePtr OpPC,
+ const CallExpr *Call, unsigned ID) {
+ assert(Call->getNumArgs() == 2);
+
+ APSInt ImmAPS = popToAPSInt(S, Call->getArg(1));
+ const Pointer &Vec = S.Stk.pop<Pointer>();
+ if (!Vec.getFieldDesc()->isPrimitiveArray())
+ return false;
+
+ unsigned NumElems = Vec.getNumElems();
+ unsigned Index =
+ static_cast<unsigned>(ImmAPS.getZExtValue() & (NumElems - 1));
+
+ PrimType ElemPT = Vec.getFieldDesc()->getPrimType();
+ // FIXME(#161685): Replace float+int split with a numeric-only type switch
+ if (ElemPT == PT_Float) {
+ S.Stk.push<Floating>(Vec.elem<Floating>(Index));
+ return true;
+ }
+ INT_TYPE_SWITCH_NO_BOOL(ElemPT, {
+ APSInt V = Vec.elem<T>(Index).toAPSInt();
+ pushInteger(S, V, Call->getType());
+ });
+
+ return true;
+}
+
+static bool interp__builtin_vec_set(InterpState &S, CodePtr OpPC,
+ const CallExpr *Call, unsigned ID) {
+ assert(Call->getNumArgs() == 3);
+
+ APSInt ImmAPS = popToAPSInt(S, Call->getArg(2));
+ APSInt ValAPS = popToAPSInt(S, Call->getArg(1));
+
+ const Pointer &Base = S.Stk.pop<Pointer>();
+ if (!Base.getFieldDesc()->isPrimitiveArray())
+ return false;
+
+ const Pointer &Dst = S.Stk.peek<Pointer>();
+
+ unsigned NumElems = Base.getNumElems();
+ unsigned Index =
+ static_cast<unsigned>(ImmAPS.getZExtValue() & (NumElems - 1));
+
+ PrimType ElemPT = Base.getFieldDesc()->getPrimType();
+ INT_TYPE_SWITCH_NO_BOOL(ElemPT, {
+ for (unsigned I = 0; I != NumElems; ++I)
+ Dst.elem<T>(I) = Base.elem<T>(I);
+ Dst.elem<T>(Index) = static_cast<T>(ValAPS);
+ });
+
+ Dst.initializeAllElements();
+ return true;
+}
+
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
uint32_t BuiltinID) {
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -3606,6 +3705,21 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case X86::BI__builtin_ia32_selectpd_512:
return interp__builtin_select(S, OpPC, Call);
+ case X86::BI__builtin_ia32_pshuflw:
+ case X86::BI__builtin_ia32_pshuflw256:
+ case X86::BI__builtin_ia32_pshuflw512:
+ return interp__builtin_ia32_pshuf(S, OpPC, Call, false);
+
+ case X86::BI__builtin_ia32_pshufhw:
+ case X86::BI__builtin_ia32_pshufhw256:
+ case X86::BI__builtin_ia32_pshufhw512:
+ return interp__builtin_ia32_pshuf(S, OpPC, Call, true);
+
+ case X86::BI__builtin_ia32_pshufd:
+ case X86::BI__builtin_ia32_pshufd256:
+ case X86::BI__builtin_ia32_pshufd512:
+ return interp__builtin_ia32_pshuf(S, OpPC, Call, false);
+
case X86::BI__builtin_ia32_kandqi:
case X86::BI__builtin_ia32_kandhi:
case X86::BI__builtin_ia32_kandsi:
@@ -3686,6 +3800,29 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case X86::BI__builtin_ia32_insert128i256:
return interp__builtin_x86_insert_subvector(S, OpPC, Call, BuiltinID);
+ case X86::BI__builtin_ia32_vec_ext_v4hi:
+ case X86::BI__builtin_ia32_vec_ext_v16qi:
+ case X86::BI__builtin_ia32_vec_ext_v8hi:
+ case X86::BI__builtin_ia32_vec_ext_v4si:
+ case X86::BI__builtin_ia32_vec_ext_v2di:
+ case X86::BI__builtin_ia32_vec_ext_v32qi:
+ case X86::BI__builtin_ia32_vec_ext_v16hi:
+ case X86::BI__builtin_ia32_vec_ext_v8si:
+ case X86::BI__builtin_ia32_vec_ext_v4di:
+ case X86::BI__builtin_ia32_vec_ext_v4sf:
+ return interp__builtin_vec_ext(S, OpPC, Call, BuiltinID);
+
+ case X86::BI__builtin_ia32_vec_set_v4hi:
+ case X86::BI__builtin_ia32_vec_set_v16qi:
+ case X86::BI__builtin_ia32_vec_set_v8hi:
+ case X86::BI__builtin_ia32_vec_set_v4si:
+ case X86::BI__builtin_ia32_vec_set_v2di:
+ case X86::BI__builtin_ia32_vec_set_v32qi:
+ case X86::BI__builtin_ia32_vec_set_v16hi:
+ case X86::BI__builtin_ia32_vec_set_v8si:
+ case X86::BI__builtin_ia32_vec_set_v4di:
+ return interp__builtin_vec_set(S, OpPC, Call, BuiltinID);
+
default:
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_invalid_subexpr_in_const_expr)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b706b14..7bf28d9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11615,6 +11615,60 @@ static bool evalPackBuiltin(const CallExpr *E, EvalInfo &Info, APValue &Result,
return true;
}
+static bool evalPshufBuiltin(EvalInfo &Info, const CallExpr *Call,
+ bool IsShufHW, APValue &Out) {
+ APValue Vec;
+ APSInt Imm;
+ if (!EvaluateAsRValue(Info, Call->getArg(0), Vec))
+ return false;
+ if (!EvaluateInteger(Call->getArg(1), Imm, Info))
+ return false;
+
+ const auto *VT = Call->getType()->getAs<VectorType>();
+ if (!VT)
+ return false;
+
+ QualType ElemT = VT->getElementType();
+ unsigned ElemBits = Info.Ctx.getTypeSize(ElemT);
+ unsigned NumElts = VT->getNumElements();
+
+ unsigned LaneBits = 128u;
+ unsigned LaneElts = LaneBits / ElemBits;
+ if (!LaneElts || (NumElts % LaneElts) != 0)
+ return false;
+
+ uint8_t Ctl = static_cast<uint8_t>(Imm.getZExtValue());
+
+ SmallVector<APValue, 32> ResultElements;
+ ResultElements.reserve(NumElts);
+
+ for (unsigned Idx = 0; Idx != NumElts; Idx++) {
+ unsigned LaneBase = (Idx / LaneElts) * LaneElts;
+ unsigned LaneIdx = Idx % LaneElts;
+ unsigned SrcIdx = Idx;
+ unsigned Sel = (Ctl >> (2 * LaneIdx)) & 0x3;
+
+ if (ElemBits == 32) {
+ SrcIdx = LaneBase + Sel;
+ } else {
+ constexpr unsigned HalfSize = 4;
+ bool InHigh = LaneIdx >= HalfSize;
+ if (!IsShufHW && !InHigh) {
+ SrcIdx = LaneBase + Sel;
+ } else if (IsShufHW && InHigh) {
+ unsigned Rel = LaneIdx - HalfSize;
+ Sel = (Ctl >> (2 * Rel)) & 0x3;
+ SrcIdx = LaneBase + HalfSize + Sel;
+ }
+ }
+
+ ResultElements.push_back(Vec.getVectorElt(SrcIdx));
+ }
+
+ Out = APValue(ResultElements.data(), ResultElements.size());
+ return true;
+}
+
bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
if (!IsConstantEvaluatedBuiltinCall(E))
return ExprEvaluatorBaseTy::VisitCallExpr(E);
@@ -11868,7 +11922,6 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
-
case clang::X86::BI__builtin_ia32_vprotbi:
case clang::X86::BI__builtin_ia32_vprotdi:
case clang::X86::BI__builtin_ia32_vprotqi:
@@ -12087,6 +12140,34 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
+
+ case X86::BI__builtin_ia32_pshuflw:
+ case X86::BI__builtin_ia32_pshuflw256:
+ case X86::BI__builtin_ia32_pshuflw512: {
+ APValue R;
+ if (!evalPshufBuiltin(Info, E, false, R))
+ return false;
+ return Success(R, E);
+ }
+
+ case X86::BI__builtin_ia32_pshufhw:
+ case X86::BI__builtin_ia32_pshufhw256:
+ case X86::BI__builtin_ia32_pshufhw512: {
+ APValue R;
+ if (!evalPshufBuiltin(Info, E, true, R))
+ return false;
+ return Success(R, E);
+ }
+
+ case X86::BI__builtin_ia32_pshufd:
+ case X86::BI__builtin_ia32_pshufd256:
+ case X86::BI__builtin_ia32_pshufd512: {
+ APValue R;
+ if (!evalPshufBuiltin(Info, E, false, R))
+ return false;
+ return Success(R, E);
+ }
+
case Builtin::BI__builtin_elementwise_clzg:
case Builtin::BI__builtin_elementwise_ctzg: {
APValue SourceLHS;
@@ -12235,6 +12316,41 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
+
+ case clang::X86::BI__builtin_ia32_vec_set_v4hi:
+ case clang::X86::BI__builtin_ia32_vec_set_v16qi:
+ case clang::X86::BI__builtin_ia32_vec_set_v8hi:
+ case clang::X86::BI__builtin_ia32_vec_set_v4si:
+ case clang::X86::BI__builtin_ia32_vec_set_v2di:
+ case clang::X86::BI__builtin_ia32_vec_set_v32qi:
+ case clang::X86::BI__builtin_ia32_vec_set_v16hi:
+ case clang::X86::BI__builtin_ia32_vec_set_v8si:
+ case clang::X86::BI__builtin_ia32_vec_set_v4di: {
+ APValue VecVal;
+ APSInt Scalar, IndexAPS;
+ if (!EvaluateVector(E->getArg(0), VecVal, Info) ||
+ !EvaluateInteger(E->getArg(1), Scalar, Info) ||
+ !EvaluateInteger(E->getArg(2), IndexAPS, Info))
+ return false;
+
+ QualType ElemTy = E->getType()->castAs<VectorType>()->getElementType();
+ unsigned ElemWidth = Info.Ctx.getIntWidth(ElemTy);
+ bool ElemUnsigned = ElemTy->isUnsignedIntegerOrEnumerationType();
+ Scalar.setIsUnsigned(ElemUnsigned);
+ APSInt ElemAPS = Scalar.extOrTrunc(ElemWidth);
+ APValue ElemAV(ElemAPS);
+
+ unsigned NumElems = VecVal.getVectorLength();
+ unsigned Index =
+ static_cast<unsigned>(IndexAPS.getZExtValue() & (NumElems - 1));
+
+ SmallVector<APValue, 4> Elems;
+ Elems.reserve(NumElems);
+ for (unsigned ElemNum = 0; ElemNum != NumElems; ++ElemNum)
+ Elems.push_back(ElemNum == Index ? ElemAV : VecVal.getVectorElt(ElemNum));
+
+ return Success(APValue(Elems.data(), NumElems), E);
+ }
}
}
@@ -14822,6 +14938,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return HandleMaskBinOp(
[](const APSInt &LHS, const APSInt &RHS) { return LHS + RHS; });
}
+
+ case clang::X86::BI__builtin_ia32_vec_ext_v4hi:
+ case clang::X86::BI__builtin_ia32_vec_ext_v16qi:
+ case clang::X86::BI__builtin_ia32_vec_ext_v8hi:
+ case clang::X86::BI__builtin_ia32_vec_ext_v4si:
+ case clang::X86::BI__builtin_ia32_vec_ext_v2di:
+ case clang::X86::BI__builtin_ia32_vec_ext_v32qi:
+ case clang::X86::BI__builtin_ia32_vec_ext_v16hi:
+ case clang::X86::BI__builtin_ia32_vec_ext_v8si:
+ case clang::X86::BI__builtin_ia32_vec_ext_v4di: {
+ APValue Vec;
+ APSInt IdxAPS;
+ if (!EvaluateVector(E->getArg(0), Vec, Info) ||
+ !EvaluateInteger(E->getArg(1), IdxAPS, Info))
+ return false;
+ unsigned N = Vec.getVectorLength();
+ unsigned Idx = static_cast<unsigned>(IdxAPS.getZExtValue() & (N - 1));
+ return Success(Vec.getVectorElt(Idx).getInt(), E);
+ }
}
}
@@ -16638,6 +16773,17 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
(void)Result.fusedMultiplyAdd(SourceY, SourceZ, RM);
return true;
}
+
+ case clang::X86::BI__builtin_ia32_vec_ext_v4sf: {
+ APValue Vec;
+ APSInt IdxAPS;
+ if (!EvaluateVector(E->getArg(0), Vec, Info) ||
+ !EvaluateInteger(E->getArg(1), IdxAPS, Info))
+ return false;
+ unsigned N = Vec.getVectorLength();
+ unsigned Idx = static_cast<unsigned>(IdxAPS.getZExtValue() & (N - 1));
+ return Success(Vec.getVectorElt(Idx), E);
+ }
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
index ea6ea2c..bbc45e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
@@ -427,22 +427,20 @@ void OpenACCRecipeBuilderBase::makeBoundsInit(
cgf.emitAutoVarInit(tempDeclEmission);
}
-// TODO: OpenACC: When we get this implemented for the reduction/firstprivate,
-// this might end up re-merging with createRecipeInitCopy. For now, keep it
-// separate until we're sure what everything looks like to keep this as clean
-// as possible.
-void OpenACCRecipeBuilderBase::createPrivateInitRecipe(
+// TODO: OpenACC: when we start doing firstprivate for array/vlas/etc, we
+// probably need to do a little work about the 'init' calls to put it in 'copy'
+// region instead.
+void OpenACCRecipeBuilderBase::createInitRecipe(
mlir::Location loc, mlir::Location locEnd, SourceRange exprRange,
- mlir::Value mainOp, mlir::acc::PrivateRecipeOp recipe, size_t numBounds,
+ mlir::Value mainOp, mlir::Region &recipeInitRegion, size_t numBounds,
llvm::ArrayRef<QualType> boundTypes, const VarDecl *allocaDecl,
QualType origType) {
assert(allocaDecl && "Required recipe variable not set?");
CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, allocaDecl};
- mlir::Block *block =
- createRecipeBlock(recipe.getInitRegion(), mainOp.getType(), loc,
- numBounds, /*isInit=*/true);
- builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
+ mlir::Block *block = createRecipeBlock(recipeInitRegion, mainOp.getType(),
+ loc, numBounds, /*isInit=*/true);
+ builder.setInsertionPointToEnd(&recipeInitRegion.back());
CIRGenFunction::LexicalScope ls(cgf, loc, block);
const Type *allocaPointeeType =
@@ -458,7 +456,7 @@ void OpenACCRecipeBuilderBase::createPrivateInitRecipe(
// Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
// emit an error to tell us. However, emitting those errors during
// production is a violation of the standard, so we cannot do them.
- cgf.cgm.errorNYI(exprRange, "private default-init recipe");
+ cgf.cgm.errorNYI(exprRange, "private/reduction default-init recipe");
}
if (!numBounds) {
@@ -469,7 +467,7 @@ void OpenACCRecipeBuilderBase::createPrivateInitRecipe(
cgf.emitAutoVarInit(tempDeclEmission);
} else {
mlir::Value alloca = makeBoundsAlloca(
- block, exprRange, loc, "openacc.private.init", numBounds, boundTypes);
+ block, exprRange, loc, allocaDecl->getName(), numBounds, boundTypes);
// If the initializer is trivial, there is nothing to do here, so save
// ourselves some effort.
@@ -521,10 +519,10 @@ void OpenACCRecipeBuilderBase::createFirstprivateRecipeCopy(
// doesn't restore it aftewards.
void OpenACCRecipeBuilderBase::createReductionRecipeCombiner(
mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp,
- mlir::acc::ReductionRecipeOp recipe) {
- mlir::Block *block = builder.createBlock(
- &recipe.getCombinerRegion(), recipe.getCombinerRegion().end(),
- {mainOp.getType(), mainOp.getType()}, {loc, loc});
+ mlir::acc::ReductionRecipeOp recipe, size_t numBounds) {
+ mlir::Block *block =
+ createRecipeBlock(recipe.getCombinerRegion(), mainOp.getType(), loc,
+ numBounds, /*isInit=*/false);
builder.setInsertionPointToEnd(&recipe.getCombinerRegion().back());
CIRGenFunction::LexicalScope ls(cgf, loc, block);
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
index a05b0bd..21707ad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
@@ -64,13 +64,13 @@ protected:
// doesn't restore it aftewards.
void createReductionRecipeCombiner(mlir::Location loc, mlir::Location locEnd,
mlir::Value mainOp,
- mlir::acc::ReductionRecipeOp recipe);
- void createPrivateInitRecipe(mlir::Location loc, mlir::Location locEnd,
- SourceRange exprRange, mlir::Value mainOp,
- mlir::acc::PrivateRecipeOp recipe,
- size_t numBounds,
- llvm::ArrayRef<QualType> boundTypes,
- const VarDecl *allocaDecl, QualType origType);
+ mlir::acc::ReductionRecipeOp recipe,
+ size_t numBounds);
+ void createInitRecipe(mlir::Location loc, mlir::Location locEnd,
+ SourceRange exprRange, mlir::Value mainOp,
+ mlir::Region &recipeInitRegion, size_t numBounds,
+ llvm::ArrayRef<QualType> boundTypes,
+ const VarDecl *allocaDecl, QualType origType);
void createRecipeDestroySection(mlir::Location loc, mlir::Location locEnd,
mlir::Value mainOp, CharUnits alignment,
@@ -224,10 +224,10 @@ public:
// TODO: OpenACC: This is a bit of a hackery to get this to not change for
// the non-private recipes. This will be removed soon, when we get this
// 'right' for firstprivate and reduction.
- if constexpr (!std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>) {
if (numBounds) {
cgf.cgm.errorNYI(varRef->getSourceRange(),
- "firstprivate/reduction-init with bounds");
+ "firstprivate-init with bounds");
}
boundTypes = {};
numBounds = 0;
@@ -260,18 +260,25 @@ public:
insertLocation = modBuilder.saveInsertionPoint();
if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
- createPrivateInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
- recipe, numBounds, boundTypes, varRecipe,
- origType);
+ createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
+ recipe.getInitRegion(), numBounds, boundTypes, varRecipe,
+ origType);
+ } else if constexpr (std::is_same_v<RecipeTy,
+ mlir::acc::ReductionRecipeOp>) {
+ createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
+ recipe.getInitRegion(), numBounds, boundTypes, varRecipe,
+ origType);
+ createReductionRecipeCombiner(loc, locEnd, mainOp, recipe, numBounds);
} else {
+ static_assert(std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>);
+ // TODO: OpenACC: we probably want this to call createInitRecipe as well,
+ // but do so in a way that omits the 'initialization', so that we can do
+ // it separately, since it belongs in the 'copy' region. It also might
+ // need a way of getting the tempDeclEmission out of it for that purpose.
createRecipeInitCopy(loc, locEnd, varRef->getSourceRange(), mainOp,
recipe, varRecipe, temporary);
}
- if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
- createReductionRecipeCombiner(loc, locEnd, mainOp, recipe);
- }
-
if (origType.isDestructedType())
createRecipeDestroySection(
loc, locEnd, mainOp, cgf.getContext().getDeclAlign(varRecipe),
diff --git a/clang/lib/CrossTU/CrossTranslationUnit.cpp b/clang/lib/CrossTU/CrossTranslationUnit.cpp
index 847913d..0287845 100644
--- a/clang/lib/CrossTU/CrossTranslationUnit.cpp
+++ b/clang/lib/CrossTU/CrossTranslationUnit.cpp
@@ -252,9 +252,9 @@ CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
std::optional<std::string>
-CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
+CrossTranslationUnitContext::getLookupName(const Decl *D) {
SmallString<128> DeclUSR;
- bool Ret = index::generateUSRForDecl(ND, DeclUSR);
+ bool Ret = index::generateUSRForDecl(D, DeclUSR);
if (Ret)
return {};
return std::string(DeclUSR);
diff --git a/clang/lib/Headers/avx10_2bf16intrin.h b/clang/lib/Headers/avx10_2bf16intrin.h
index 5bcec4b..765cd68 100644
--- a/clang/lib/Headers/avx10_2bf16intrin.h
+++ b/clang/lib/Headers/avx10_2bf16intrin.h
@@ -519,34 +519,34 @@ _mm_maskz_min_pbh(__mmask8 __U, __m128bh __A, __m128bh __B) {
(__mmask8)__U, (__v8bf)_mm_min_pbh(__A, __B), (__v8bf)_mm_setzero_pbh());
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comieq_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16eq((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comieq_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16eq((__v8bf)__A, (__v8bf)__B);
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comilt_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16lt((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comilt_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16lt((__v8bf)__A, (__v8bf)__B);
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comile_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16le((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comile_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16le((__v8bf)__A, (__v8bf)__B);
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comigt_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16gt((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comigt_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16gt((__v8bf)__A, (__v8bf)__B);
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comige_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16ge((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comige_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16ge((__v8bf)__A, (__v8bf)__B);
}
-static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comineq_sbh(__m128bh A,
- __m128bh B) {
- return __builtin_ia32_vcomisbf16neq((__v8bf)A, (__v8bf)B);
+static __inline__ int __DEFAULT_FN_ATTRS128 _mm_comineq_sbh(__m128bh __A,
+ __m128bh __B) {
+ return __builtin_ia32_vcomisbf16neq((__v8bf)__A, (__v8bf)__B);
}
#define _mm256_cmp_pbh_mask(__A, __B, __P) \
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index dc6d232..8413090 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -12,9 +12,11 @@
#include "clang/Sema/SemaConcept.h"
#include "TreeTransform.h"
+#include "clang/AST/ASTConcept.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"
@@ -27,7 +29,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringExtras.h"
-#include <optional>
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace sema;
@@ -85,7 +87,7 @@ public:
OK_Ordinary, Loc, FPOptionsOverride{});
}
};
-}
+} // namespace
bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
Token NextToken, bool *PossibleNonPrimary,
@@ -146,14 +148,14 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
Diag(ConstraintExpression->getExprLoc(),
- diag::err_non_bool_atomic_constraint) << Type
- << ConstraintExpression->getSourceRange();
+ diag::err_non_bool_atomic_constraint)
+ << Type << ConstraintExpression->getSourceRange();
CheckForNonPrimary();
return false;
}
if (PossibleNonPrimary)
- *PossibleNonPrimary = false;
+ *PossibleNonPrimary = false;
return true;
}
@@ -164,52 +166,315 @@ struct SatisfactionStackRAII {
SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND,
const llvm::FoldingSetNodeID &FSNID)
: SemaRef(SemaRef) {
- if (ND) {
+ if (ND) {
SemaRef.PushSatisfactionStackEntry(ND, FSNID);
Inserted = true;
- }
+ }
}
~SatisfactionStackRAII() {
- if (Inserted)
- SemaRef.PopSatisfactionStackEntry();
+ if (Inserted)
+ SemaRef.PopSatisfactionStackEntry();
}
};
} // namespace
-static bool
-DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
- const NamedDecl *Templ, const Expr *E,
- const MultiLevelTemplateArgumentList &MLTAL) {
+static bool DiagRecursiveConstraintEval(
+ Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
+ const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
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 (MLTAL) {
+ for (const auto &List : *MLTAL)
+ for (const auto &TemplateArg : List.Args)
+ S.Context.getCanonicalTemplateArgument(TemplateArg)
+ .Profile(ID, S.Context);
+ }
if (S.SatisfactionStackContains(Templ, ID)) {
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
<< E << E->getSourceRange();
return true;
}
-
return false;
}
-static ExprResult EvaluateAtomicConstraint(
- Sema &S, const Expr *AtomicExpr, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &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=*/std::nullopt,
+ /*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;
+ }
+
+ bool AlreadyTransformed(QualType T) {
+ if (T.isNull())
+ return true;
+
+ if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
+ T->containsUnexpandedParameterPack())
+ return false;
+ return true;
+ }
+};
+} // namespace
+
+namespace {
+
+// FIXME: Convert it to DynamicRecursiveASTVisitor
+class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
+ using inherited = RecursiveASTVisitor<HashParameterMapping>;
+ friend inherited;
+
+ Sema &SemaRef;
+ const MultiLevelTemplateArgumentList &TemplateArgs;
+ llvm::FoldingSetNodeID &ID;
+ llvm::SmallVector<TemplateArgument, 10> UsedTemplateArgs;
+
+ UnsignedOrNone OuterPackSubstIndex;
+
+ TemplateArgument getPackSubstitutedTemplateArgument(TemplateArgument Arg) {
+ assert(*SemaRef.ArgPackSubstIndex < Arg.pack_size());
+ Arg = Arg.pack_begin()[*SemaRef.ArgPackSubstIndex];
+ if (Arg.isPackExpansion())
+ Arg = Arg.getPackExpansionPattern();
+ return Arg;
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+
+public:
+ HashParameterMapping(Sema &SemaRef,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ llvm::FoldingSetNodeID &ID,
+ UnsignedOrNone OuterPackSubstIndex)
+ : SemaRef(SemaRef), TemplateArgs(TemplateArgs), ID(ID),
+ OuterPackSubstIndex(OuterPackSubstIndex) {}
+
+ bool VisitTemplateTypeParmType(TemplateTypeParmType *T) {
+ // A lambda expression can introduce template parameters that don't have
+ // corresponding template arguments yet.
+ if (T->getDepth() >= TemplateArgs.getNumLevels())
+ return true;
+
+ TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex());
+
+ if (T->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ assert(Arg.getKind() == TemplateArgument::Pack &&
+ "Missing argument pack");
+
+ Arg = getPackSubstitutedTemplateArgument(Arg);
+ }
+
+ UsedTemplateArgs.push_back(
+ SemaRef.Context.getCanonicalTemplateArgument(Arg));
+ return true;
+ }
+
+ bool VisitDeclRefExpr(DeclRefExpr *E) {
+ NamedDecl *D = E->getDecl();
+ NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D);
+ if (!NTTP)
+ return TraverseDecl(D);
+
+ TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
+ if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
+ assert(Arg.getKind() == TemplateArgument::Pack &&
+ "Missing argument pack");
+ Arg = getPackSubstitutedTemplateArgument(Arg);
+ }
+
+ UsedTemplateArgs.push_back(
+ SemaRef.Context.getCanonicalTemplateArgument(Arg));
+ return true;
+ }
+
+ bool VisitTypedefType(TypedefType *TT) {
+ return inherited::TraverseType(TT->desugar());
+ }
+
+ bool TraverseDecl(Decl *D) {
+ if (auto *VD = dyn_cast<ValueDecl>(D))
+ return TraverseType(VD->getType());
+
+ return inherited::TraverseDecl(D);
+ }
+
+ bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
+ // We don't care about TypeLocs. So traverse Types instead.
+ return TraverseType(TL.getType(), TraverseQualifier);
+ }
+
+ bool TraverseTagType(const TagType *T, bool TraverseQualifier) {
+ // T's parent can be dependent while T doesn't have any template arguments.
+ // We should have already traversed its qualifier.
+ // FIXME: Add an assert to catch cases where we failed to profile the
+ // concept. assert(!T->isDependentType() && "We missed a case in profiling
+ // concepts!");
+ return true;
+ }
+
+ bool TraverseInjectedClassNameType(InjectedClassNameType *T,
+ bool TraverseQualifier) {
+ return TraverseTemplateArguments(T->getTemplateArgs(SemaRef.Context));
+ }
+
+ bool TraverseTemplateArgument(const TemplateArgument &Arg) {
+ if (!Arg.containsUnexpandedParameterPack() || Arg.isPackExpansion()) {
+ // Act as if we are fully expanding this pack, if it is a PackExpansion.
+ Sema::ArgPackSubstIndexRAII _1(SemaRef, std::nullopt);
+ llvm::SaveAndRestore<UnsignedOrNone> _2(OuterPackSubstIndex,
+ std::nullopt);
+ return inherited::TraverseTemplateArgument(Arg);
+ }
+
+ Sema::ArgPackSubstIndexRAII _1(SemaRef, OuterPackSubstIndex);
+ return inherited::TraverseTemplateArgument(Arg);
+ }
+
+ void VisitConstraint(const NormalizedConstraintWithParamMapping &Constraint) {
+ if (!Constraint.hasParameterMapping()) {
+ for (const auto &List : TemplateArgs)
+ for (const TemplateArgument &Arg : List.Args)
+ SemaRef.Context.getCanonicalTemplateArgument(Arg).Profile(
+ ID, SemaRef.Context);
+ return;
+ }
+
+ llvm::ArrayRef<TemplateArgumentLoc> Mapping =
+ Constraint.getParameterMapping();
+ for (auto &ArgLoc : Mapping) {
+ TemplateArgument Canonical =
+ SemaRef.Context.getCanonicalTemplateArgument(ArgLoc.getArgument());
+ // We don't want sugars to impede the profile of cache.
+ UsedTemplateArgs.push_back(Canonical);
+ TraverseTemplateArgument(Canonical);
+ }
+
+ for (auto &Used : UsedTemplateArgs) {
+ llvm::FoldingSetNodeID R;
+ Used.Profile(R, SemaRef.Context);
+ ID.AddNodeID(R);
+ }
+ }
+};
+
+class ConstraintSatisfactionChecker {
+ Sema &S;
+ const NamedDecl *Template;
+ SourceLocation TemplateNameLoc;
+ UnsignedOrNone PackSubstitutionIndex;
+
+ ConstraintSatisfaction &Satisfaction;
+
+private:
+ ExprResult
+ EvaluateAtomicConstraint(const Expr *AtomicExpr,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ // XXX: It is SLOW! Use it very carefully.
+ std::optional<MultiLevelTemplateArgumentList> SubstitutionInTemplateArguments(
+ const NormalizedConstraintWithParamMapping &Constraint,
+ MultiLevelTemplateArgumentList MLTAL,
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost);
+
+ ExprResult EvaluateSlow(const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult EvaluateSlow(const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult EvaluateSlow(const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ unsigned int Size);
+
+ ExprResult Evaluate(const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+ ExprResult Evaluate(const CompoundConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
+public:
+ ConstraintSatisfactionChecker(Sema &SemaRef, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc,
+ UnsignedOrNone PackSubstitutionIndex,
+ ConstraintSatisfaction &Satisfaction)
+ : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc),
+ PackSubstitutionIndex(PackSubstitutionIndex),
+ Satisfaction(Satisfaction) {}
+
+ ExprResult Evaluate(const NormalizedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL);
+};
+
+StringRef allocateStringFromConceptDiagnostic(const Sema &S,
+ const PartialDiagnostic Diag) {
+ SmallString<128> DiagString;
+ DiagString = ": ";
+ Diag.EmitToString(S.getDiagnostics(), DiagString);
+ return S.getASTContext().backupStr(DiagString);
+}
+
+} // namespace
+
+ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
+ const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) {
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
Sema::ReuseLambdaContextDecl);
+ llvm::FoldingSetNodeID ID;
+ if (Template &&
+ DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) {
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = true;
+ return ExprEmpty();
+ }
+ SatisfactionStackRAII StackRAII(S, Template, ID);
+
// Atomic constraint - substitute arguments and check satisfaction.
- ExprResult SubstitutedExpression;
+ ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
{
TemplateDeductionInfo Info(TemplateNameLoc);
Sema::InstantiatingTemplate Inst(
@@ -220,16 +485,6 @@ static ExprResult EvaluateAtomicConstraint(
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 =
@@ -247,21 +502,16 @@ static ExprResult EvaluateAtomicConstraint(
PartialDiagnosticAt SubstDiag{SourceLocation(),
PartialDiagnostic::NullDiagnostic()};
Info.takeSFINAEDiagnostic(SubstDiag);
- // FIXME: Concepts: This is an unfortunate consequence of there
+ // FIXME: 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(
- new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
- SubstDiag.first, StringRef(Mem, MessageSize)});
+ new (S.Context) ConstraintSubstitutionDiagnostic{
+ SubstDiag.first,
+ allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
Satisfaction.IsSatisfied = false;
return ExprEmpty();
}
@@ -289,216 +539,94 @@ static ExprResult EvaluateAtomicConstraint(
return SubstitutedExpression;
}
-static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
- Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
-
- // We should ignore errors in the presence of packs of different size.
- Sema::SFINAETrap Trap(S);
-
- Expr *Pattern = FE->getPattern();
+std::optional<MultiLevelTemplateArgumentList>
+ConstraintSatisfactionChecker::SubstitutionInTemplateArguments(
+ const NormalizedConstraintWithParamMapping &Constraint,
+ MultiLevelTemplateArgumentList MLTAL,
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
- bool Expand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = FE->getNumExpansions();
- if (S.CheckParameterPacksForExpansion(
- FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
- /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion,
- NumExpansions) ||
- !Expand || RetainExpansion)
- return std::nullopt;
+ if (!Constraint.hasParameterMapping())
+ return std::move(MLTAL);
- if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
- S.Diag(FE->getEllipsisLoc(),
- clang::diag::err_fold_expression_limit_exceeded)
- << *NumExpansions << S.getLangOpts().BracketDepth
- << FE->getSourceRange();
- S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth);
+ TemplateDeductionInfo Info(Constraint.getBeginLoc());
+ Sema::InstantiatingTemplate Inst(
+ S, Constraint.getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintSubstitution{},
+ // FIXME: improve const-correctness of InstantiatingTemplate
+ const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange());
+ if (Inst.isInvalid())
return std::nullopt;
- }
- return NumExpansions;
-}
-
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction);
-
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *LHS, OverloadedOperatorKind Op, const Expr *RHS,
- const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
- ExprResult LHSRes = calculateConstraintSatisfaction(
- S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
- if (LHSRes.isInvalid())
- return ExprError();
-
- bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
- if (Op == clang::OO_PipePipe && 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.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- if (Op == clang::OO_AmpAmp && !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.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- ExprResult RHSRes = calculateConstraintSatisfaction(
- S, RHS, Template, TemplateNameLoc, MLTAL, 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 (Op == clang::OO_PipePipe && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end());
- }
-
- if (!LHSRes.isUsable() || !RHSRes.isUsable())
- return ExprEmpty();
-
- return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(),
- BinaryOperator::getOverloadedOpcode(Op),
- S.Context.BoolTy, VK_PRValue, OK_Ordinary,
- LHS->getBeginLoc(), FPOptionsOverride{});
-}
-
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd;
- size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
- ExprResult Out;
- if (FE->isLeftFold() && FE->getInit()) {
- Out = calculateConstraintSatisfaction(S, FE->getInit(), Template,
- TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
+ Sema::SFINAETrap Trap(S);
- // If the first clause of a conjunction is not satisfied,
- // or if the first clause of a disjection is satisfied,
- // we have established satisfaction of the whole constraint
- // and we should not continue further.
- if (Conjunction != Satisfaction.IsSatisfied)
- return Out;
- }
- UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
- S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (!NumExpansions)
- return ExprError();
- for (unsigned I = 0; I < *NumExpansions; I++) {
- Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Res.isInvalid())
- return ExprError();
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- if (!Conjunction && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd,
- Satisfaction.Details.end());
- }
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
- }
- if (Conjunction != IsRHSSatisfied)
- return Out;
+ TemplateArgumentListInfo SubstArgs;
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
+
+ if (S.SubstTemplateArgumentsInParameterMapping(
+ Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
+ SubstArgs, /*BuildPackExpansionTypes=*/true)) {
+ Satisfaction.IsSatisfied = false;
+ return std::nullopt;
}
- if (FE->isRightFold() && FE->getInit()) {
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
-
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD = const_cast<TemplateDecl *>(
+ cast<TemplateDecl>(Constraint.getConstraintDecl()));
+ if (S.CheckTemplateArgumentList(TD, Constraint.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
+ return std::nullopt;
+ const NormalizedConstraint::OccurenceList &Used =
+ Constraint.mappingOccurenceList();
+ SubstitutedOuterMost =
+ llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+ unsigned Offset = 0;
+ for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
+ TemplateArgument Arg;
+ if (Used[I])
+ Arg = S.Context.getCanonicalTemplateArgument(
+ CTAI.SugaredConverted[MappedIndex++]);
+ if (I < SubstitutedOuterMost.size()) {
+ SubstitutedOuterMost[I] = Arg;
+ Offset = I + 1;
+ } else {
+ SubstitutedOuterMost.push_back(Arg);
+ Offset = SubstitutedOuterMost.size();
}
}
+ if (Offset < SubstitutedOuterMost.size())
+ SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset);
- if (Out.isUnset()) {
- Satisfaction.IsSatisfied = Conjunction;
- Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator());
- }
- return Out;
+ MLTAL.replaceOutermostTemplateArguments(
+ const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
+ SubstitutedOuterMost);
+ return std::move(MLTAL);
}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
-
- if (LogicalBinOp BO = ConstraintExpr)
- return calculateConstraintSatisfaction(
- S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
- 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(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- }
-
- if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr);
- FE && S.getLangOpts().CPlusPlus26 &&
- (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
- FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
- return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprEmpty();
}
- // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints.
-
- // An atomic constraint expression
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex);
ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint(
- S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction);
+ Constraint.getConstraintExpr(), *SubstitutedArgs);
if (SubstitutedAtomicExpr.isInvalid())
return ExprError();
- if (!SubstitutedAtomicExpr.isUsable())
+ if (SubstitutedAtomicExpr.isUnset())
// Evaluator has decided satisfaction without yielding an expression.
return ExprEmpty();
@@ -512,16 +640,16 @@ static ExprResult calculateConstraintSatisfaction(
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(
- new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+ new (S.Context) ConstraintSubstitutionDiagnostic{
SubstitutedAtomicExpr.get()->getBeginLoc(),
- StringRef(Mem, MessageSize)});
+ allocateStringFromConceptDiagnostic(S, Msg)});
+ return SubstitutedAtomicExpr;
+ }
+
+ if (SubstitutedAtomicExpr.get()->isValueDependent()) {
+ Satisfaction.IsSatisfied = true;
+ Satisfaction.ContainsErrors = false;
return SubstitutedAtomicExpr;
}
@@ -552,21 +680,384 @@ static ExprResult calculateConstraintSatisfaction(
return SubstitutedAtomicExpr;
}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
- ConstraintSatisfaction &Satisfaction) {
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const AtomicConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ unsigned Size = Satisfaction.Details.size();
+ llvm::FoldingSetNodeID ID;
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
+ ID.AddPointer(Constraint.getConstraintExpr());
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
+ .VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size,
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ ExprResult E = EvaluateSlow(Constraint, MLTAL);
+
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = E;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+
+ return E;
+}
+
+UnsignedOrNone
+ConstraintSatisfactionChecker::EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ // We should ignore errors in the presence of packs of different size.
+ Sema::SFINAETrap Trap(S);
+
+ Expr *Pattern = const_cast<Expr *>(FE.getPattern());
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions(std::nullopt);
+ if (S.CheckParameterPacksForExpansion(
+ Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
+ /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion,
+ NumExpansions) ||
+ !Expand || RetainExpansion)
+ return std::nullopt;
+
+ if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
+ S.Diag(Pattern->getExprLoc(),
+ clang::diag::err_fold_expression_limit_exceeded)
+ << *NumExpansions << S.getLangOpts().BracketDepth
+ << Pattern->getSourceRange();
+ S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth);
+ return std::nullopt;
+ }
+ return NumExpansions;
+}
- return calculateConstraintSatisfaction(S, ConstraintExpr, Template,
- TemplateNameLoc, MLTAL, Satisfaction);
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ bool Conjunction = Constraint.getFoldOperator() ==
+ FoldExpandedConstraint::FoldOperatorKind::And;
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ // FIXME: Is PackSubstitutionIndex correct?
+ llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex);
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(
+ static_cast<const NormalizedConstraintWithParamMapping &>(Constraint),
+ MLTAL, SubstitutedOuterMost);
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprError();
+ }
+
+ ExprResult Out;
+ UnsignedOrNone NumExpansions =
+ EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs);
+ if (!NumExpansions)
+ return ExprEmpty();
+
+ if (*NumExpansions == 0) {
+ Satisfaction.IsSatisfied = Conjunction;
+ return ExprEmpty();
+ }
+
+ for (unsigned I = 0; I < *NumExpansions; I++) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = false;
+ ExprResult Expr =
+ ConstraintSatisfactionChecker(S, Template, TemplateNameLoc,
+ UnsignedOrNone(I), Satisfaction)
+ .Evaluate(Constraint.getNormalizedPattern(), *SubstitutedArgs);
+ if (Expr.isUsable()) {
+ if (Out.isUnset())
+ Out = Expr;
+ else
+ Out = BinaryOperator::Create(S.Context, Out.get(), Expr.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ Constraint.getBeginLoc(),
+ FPOptionsOverride{});
+ } else {
+ assert(!Satisfaction.IsSatisfied);
+ }
+ if (!Conjunction && Satisfaction.IsSatisfied) {
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
+ Satisfaction.Details.end());
+ break;
+ }
+ if (Satisfaction.IsSatisfied != Conjunction)
+ return Out;
+ }
+
+ return Out;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const FoldExpandedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ llvm::FoldingSetNodeID ID;
+ ID.AddPointer(Constraint.getPattern());
+ HashParameterMapping(S, MLTAL, ID, std::nullopt).VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.end(),
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ unsigned Size = Satisfaction.Details.size();
+
+ ExprResult E = EvaluateSlow(Constraint, MLTAL);
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = E;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+ return E;
+}
+
+ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
+ const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) {
+ const ConceptReference *ConceptId = Constraint.getConceptId();
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost);
+
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ // FIXME: diagnostics?
+ return ExprError();
+ }
+
+ Sema::SFINAETrap Trap(S);
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
+
+ const ASTTemplateArgumentListInfo *Ori =
+ ConceptId->getTemplateArgsAsWritten();
+ TemplateDeductionInfo Info(TemplateNameLoc);
+ Sema::InstantiatingTemplate _(
+ S, TemplateNameLoc, Sema::InstantiatingTemplate::ConstraintSubstitution{},
+ const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange());
+
+ TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+ if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, OutArgs) ||
+ Trap.hasErrorOccurred()) {
+ Satisfaction.IsSatisfied = false;
+ if (!Trap.hasErrorOccurred())
+ return ExprError();
+
+ PartialDiagnosticAt SubstDiag{SourceLocation(),
+ PartialDiagnostic::NullDiagnostic()};
+ Info.takeSFINAEDiagnostic(SubstDiag);
+ // FIXME: 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.
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ new (S.Context) ConstraintSubstitutionDiagnostic{
+ SubstDiag.first,
+ allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
+ return ExprError();
+ }
+
+ CXXScopeSpec SS;
+ SS.Adopt(ConceptId->getNestedNameSpecifierLoc());
+
+ ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
+ SS, ConceptId->getTemplateKWLoc(), ConceptId->getConceptNameInfo(),
+ ConceptId->getFoundDecl(), ConceptId->getNamedConcept(), &OutArgs,
+ /*DoCheckConstraintSatisfaction=*/false);
+
+ if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
+ return ExprError();
+
+ if (Size != Satisfaction.Details.size()) {
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ UnsatisfiedConstraintRecord(
+ SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
+ ->getConceptReference()));
+ }
+ return SubstitutedConceptId;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const ConceptIdConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ const ConceptReference *ConceptId = Constraint.getConceptId();
+
+ UnsignedOrNone OuterPackSubstIndex =
+ Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex;
+
+ Sema::InstantiatingTemplate _(S, ConceptId->getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ ConceptId->getNamedConcept(),
+ MLTAL.getInnermost(),
+ Constraint.getSourceRange());
+
+ unsigned Size = Satisfaction.Details.size();
+
+ ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
+
+ if (!E.isUsable()) {
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, ConceptId);
+ return E;
+ }
+
+ // ConceptIdConstraint is only relevant for diagnostics,
+ // so if the normalized constraint is satisfied, we should not
+ // substitute into the constraint.
+ if (Satisfaction.IsSatisfied)
+ return E;
+
+ llvm::FoldingSetNodeID ID;
+ ID.AddPointer(Constraint.getConceptId());
+ ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation());
+ HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex)
+ .VisitConstraint(Constraint);
+
+ if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
+ Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
+
+ auto &Cached = Iter->second.Satisfaction;
+ Satisfaction.ContainsErrors = Cached.ContainsErrors;
+ Satisfaction.IsSatisfied = Cached.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size,
+ Cached.Details.begin(), Cached.Details.end());
+ return Iter->second.SubstExpr;
+ }
+
+ ExprResult CE = EvaluateSlow(Constraint, MLTAL, Size);
+ if (CE.isInvalid())
+ return E;
+ UnsubstitutedConstraintSatisfactionCacheResult Cache;
+ Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors;
+ Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied;
+ std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(),
+ std::back_inserter(Cache.Satisfaction.Details));
+ Cache.SubstExpr = CE;
+ S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)});
+ return CE;
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const CompoundConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+ bool Conjunction =
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
+
+ ExprResult LHS = Evaluate(Constraint.getLHS(), MLTAL);
+
+ if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
+ return LHS;
+
+ if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
+ !Satisfaction.ContainsErrors)
+ return LHS;
+
+ Satisfaction.ContainsErrors = false;
+ Satisfaction.IsSatisfied = false;
+
+ ExprResult RHS = Evaluate(Constraint.getRHS(), MLTAL);
+
+ if (RHS.isUsable() && Satisfaction.IsSatisfied &&
+ !Satisfaction.ContainsErrors)
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
+ Satisfaction.Details.end());
+
+ if (!LHS.isUsable())
+ return RHS;
+
+ if (!RHS.isUsable())
+ return LHS;
+
+ return BinaryOperator::Create(S.Context, LHS.get(), RHS.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ Constraint.getBeginLoc(), FPOptionsOverride{});
+}
+
+ExprResult ConstraintSatisfactionChecker::Evaluate(
+ const NormalizedConstraint &Constraint,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+ switch (Constraint.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return Evaluate(static_cast<const AtomicConstraint &>(Constraint), MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return Evaluate(static_cast<const FoldExpandedConstraint &>(Constraint),
+ MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return Evaluate(static_cast<const ConceptIdConstraint &>(Constraint),
+ MLTAL);
+
+ case NormalizedConstraint::ConstraintKind::Compound:
+ return Evaluate(static_cast<const CompoundConstraint &>(Constraint), MLTAL);
+ }
}
static bool CheckConstraintSatisfaction(
Sema &S, const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &Converted,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+ Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) {
+
+ if (ConvertedExpr)
+ *ConvertedExpr = nullptr;
+
if (AssociatedConstraints.empty()) {
Satisfaction.IsSatisfied = true;
return false;
@@ -578,57 +1069,60 @@ static bool CheckConstraintSatisfaction(
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())
+ llvm::ArrayRef<TemplateArgument> Args;
+ if (TemplateArgsLists.getNumLevels() != 0)
+ Args = TemplateArgsLists.getInnermost();
+
+ std::optional<Sema::InstantiatingTemplate> SynthesisContext;
+ if (!TopLevelConceptId) {
+ SynthesisContext.emplace(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ const_cast<NamedDecl *>(Template), Args,
+ TemplateIDRange);
+ }
+
+ const NormalizedConstraint *C =
+ S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints);
+ if (!C) {
+ Satisfaction.IsSatisfied = false;
return true;
+ }
- for (const AssociatedConstraint &AC : AssociatedConstraints) {
- if (AC.isNull())
- return true;
+ if (TopLevelConceptId)
+ C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
+ const_cast<NormalizedConstraint *>(C),
+ Template, /*CSE=*/nullptr,
+ S.ArgPackSubstIndex);
- Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex);
- ExprResult Res = calculateConstraintSatisfaction(
- S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
- AC.ConstraintExpr, Satisfaction);
- if (Res.isInvalid())
- return true;
+ ExprResult Res =
+ ConstraintSatisfactionChecker(S, Template, TemplateIDRange.getBegin(),
+ S.ArgPackSubstIndex, Satisfaction)
+ .Evaluate(*C, TemplateArgsLists);
+
+ if (Res.isInvalid())
+ return true;
+
+ if (Res.isUsable() && ConvertedExpr)
+ *ConvertedExpr = Res.get();
- 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(AssociatedConstraints.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. [...]
- return false;
- }
- }
return false;
}
bool Sema::CheckConstraintSatisfaction(
- const NamedDecl *Template,
+ ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
+ const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) {
if (AssociatedConstraints.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
+ const auto *Template = Entity.dyn_cast<const NamedDecl *>();
if (!Template) {
return ::CheckConstraintSatisfaction(
- *this, nullptr, AssociatedConstraints, ConvertedConstraints,
- TemplateArgsLists, TemplateIDRange, OutSatisfaction);
+ *this, nullptr, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId);
}
// Invalid templates could make their way here. Substituting them could result
// in dependent expressions.
@@ -643,10 +1137,15 @@ bool Sema::CheckConstraintSatisfaction(
// here.
llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
for (auto List : TemplateArgsLists)
- llvm::append_range(FlattenedArgs, List.Args);
+ for (const TemplateArgument &Arg : List.Args)
+ FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg));
+
+ const NamedDecl *Owner = Template;
+ if (TopLevelConceptId)
+ Owner = TopLevelConceptId->getNamedConcept();
llvm::FoldingSetNodeID ID;
- ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
+ ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs);
void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached;
@@ -654,11 +1153,11 @@ bool Sema::CheckConstraintSatisfaction(
}
auto Satisfaction =
- std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
- if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
- ConvertedConstraints, TemplateArgsLists,
- TemplateIDRange, *Satisfaction)) {
- OutSatisfaction = *Satisfaction;
+ std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
+ if (::CheckConstraintSatisfaction(
+ *this, Template, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) {
+ OutSatisfaction = std::move(*Satisfaction);
return true;
}
@@ -688,14 +1187,18 @@ bool Sema::CheckConstraintSatisfaction(
const ConceptSpecializationExpr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
+ llvm::SmallVector<AssociatedConstraint, 1> Constraints;
+ Constraints.emplace_back(
+ ConstraintExpr->getNamedConcept()->getConstraintExpr());
+
MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
ConstraintExpr->getTemplateArguments(),
true);
- return calculateConstraintSatisfaction(
- *this, ConstraintExpr, ConstraintExpr->getNamedConcept(),
- ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction)
- .isInvalid();
+ return CheckConstraintSatisfaction(
+ ConstraintExpr->getNamedConcept(), Constraints, MLTAL,
+ ConstraintExpr->getSourceRange(), Satisfaction,
+ ConstraintExpr->getConceptReference());
}
bool Sema::SetupConstraintScope(
@@ -854,50 +1357,6 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
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=*/std::nullopt,
- /*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) {
@@ -1161,73 +1620,61 @@ bool Sema::CheckFunctionTemplateConstraints(
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::ExprRequirement *Req,
bool First) {
- assert(!Req->isSatisfied()
- && "Diagnose() can only be used on an unsatisfied requirement");
+ 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();
- if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- // A simple case - expr type is the type being constrained and the concept
- // was not provided arguments.
- Expr *e = Req->getExpr();
- S.Diag(e->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied_simple)
- << (int)First << S.Context.getReferenceQualifiedType(e)
- << ConstraintExpr->getNamedConcept();
- } else {
- S.Diag(ConstraintExpr->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied)
- << (int)First << ConstraintExpr;
- }
- S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
- break;
- }
- case concepts::ExprRequirement::SS_Satisfied:
- llvm_unreachable("We checked this above");
+ 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();
+ S.DiagnoseUnsatisfiedConstraint(ConstraintExpr);
+ 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");
+ 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");
@@ -1235,9 +1682,9 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
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;
+ 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)
@@ -1249,31 +1696,53 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
return;
}
}
-static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
- bool First = true);
+
+static void diagnoseUnsatisfiedConceptIdExpr(Sema &S,
+ const ConceptReference *Concept,
+ SourceLocation Loc, bool First) {
+ if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+ S.Diag(
+ Loc,
+ diag::
+ note_single_arg_concept_specialization_constraint_evaluated_to_false)
+ << (int)First
+ << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+ << Concept->getNamedConcept();
+ } else {
+ S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
+ << (int)First << Concept;
+ }
+}
+
+static void diagnoseUnsatisfiedConstraintExpr(
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req = nullptr);
+
+static void DiagnoseUnsatisfiedConstraint(
+ Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
+ bool First = true, concepts::NestedRequirement *Req = nullptr) {
+ for (auto &Record : Records) {
+ diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
+ Loc = {};
+ First = isa<const ConceptReference *>(Record);
+ }
+}
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
- using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
- for (auto &Record : Req->getConstraintSatisfaction()) {
- if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
- S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
- << (int)First << Req->getInvalidConstraintEntity()
- << SubstDiag->second;
- else
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast<Expr *>(),
- First);
- First = false;
- }
+ DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
+ Req->hasInvalidConstraint()
+ ? SourceLocation()
+ : Req->getConstraintExpr()->getExprLoc(),
+ First, Req);
}
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
+ const Expr *SubstExpr,
bool First) {
SubstExpr = SubstExpr->IgnoreParenImpCasts();
- if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
+ if (const 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
@@ -1319,7 +1788,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context,
Expr::SE_NoSideEffects,
/*InConstantContext=*/true);
- if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) {
+ if (!SimplifiedLHS.Diag && !SimplifiedRHS.Diag) {
S.Diag(SubstExpr->getBeginLoc(),
diag::note_atomic_constraint_evaluated_to_false_elaborated)
<< (int)First << SubstExpr
@@ -1334,22 +1803,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
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;
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
// FIXME: RequiresExpr should store dependent diagnostics.
for (concepts::Requirement *Req : RE->getRequirements())
@@ -1364,6 +1817,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
break;
}
return;
+ } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+ // Drill down concept ids treated as atomic constraints
+ S.DiagnoseUnsatisfiedConstraint(CSE, First);
+ return;
} else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr);
TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) {
assert(TTE->getNumArgs() == 2);
@@ -1379,216 +1836,332 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.DiagnoseTypeTraitDetails(SubstExpr);
}
-template <typename SubstitutionDiagnostic>
static void diagnoseUnsatisfiedConstraintExpr(
- Sema &S, 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;
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req) {
+ if (auto *Diag =
+ Record
+ .template dyn_cast<const ConstraintSubstitutionDiagnostic *>()) {
+ if (Req)
+ S.Diag(Diag->first, diag::note_nested_requirement_substitution_error)
+ << (int)First << Req->getInvalidConstraintEntity() << Diag->second;
+ else
+ S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
+ << Diag->second;
return;
}
-
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First);
+ if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) {
+ if (Loc.isInvalid())
+ Loc = Concept->getBeginLoc();
+ diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First);
+ return;
+ }
+ diagnoseWellFormedUnsatisfiedConstraintExpr(
+ S, cast<const class Expr *>(Record), First);
}
-void
-Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
- bool First) {
+void Sema::DiagnoseUnsatisfiedConstraint(
+ const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
+ bool First) {
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- for (auto &Record : Satisfaction.Details) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
- First = false;
- }
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First);
}
void Sema::DiagnoseUnsatisfiedConstraint(
- const ASTConstraintSatisfaction &Satisfaction,
- bool First) {
+ const ConceptSpecializationExpr *ConstraintExpr, bool First) {
+
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConstraintExpr->getSatisfaction();
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- for (auto &Record : Satisfaction) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
- First = false;
- }
+
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
+ ConstraintExpr->getBeginLoc(), First);
}
-const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
- const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> 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());
+namespace {
- auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
- if (CacheEntry == NormalizationCache.end()) {
- auto Normalized = NormalizedConstraint::fromAssociatedConstraints(
- *this, ConstrainedDecl, AssociatedConstraints);
- CacheEntry =
- NormalizationCache
- .try_emplace(ConstrainedDecl,
- Normalized
- ? new (Context) NormalizedConstraint(
- std::move(*Normalized))
- : nullptr)
- .first;
- }
- return CacheEntry->second;
-}
+class SubstituteParameterMappings {
+ Sema &SemaRef;
-const NormalizedConstraint *clang::getNormalizedAssociatedConstraints(
- Sema &S, const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- return S.getNormalizedAssociatedConstraints(ConstrainedDecl,
- AssociatedConstraints);
-}
+ const MultiLevelTemplateArgumentList *MLTAL;
+ const ASTTemplateArgumentListInfo *ArgsAsWritten;
-static bool
-substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- ConceptDecl *Concept,
- const MultiLevelTemplateArgumentList &MLTAL,
- const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+ bool InFoldExpr;
- if (N.isCompound()) {
- if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
- ArgsAsWritten))
- return true;
- return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
- ArgsAsWritten);
- }
+ SubstituteParameterMappings(Sema &SemaRef,
+ const MultiLevelTemplateArgumentList *MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ bool InFoldExpr)
+ : SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten),
+ InFoldExpr(InFoldExpr) {}
+
+ void buildParameterMapping(NormalizedConstraintWithParamMapping &N);
+
+ bool substitute(NormalizedConstraintWithParamMapping &N);
+
+ bool substitute(ConceptIdConstraint &CC);
+
+public:
+ SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false)
+ : SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr),
+ InFoldExpr(InFoldExpr) {}
+
+ bool substitute(NormalizedConstraint &N);
+};
- if (N.isFoldExpanded()) {
- Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
- return substituteParameterMappings(
- S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL,
- ArgsAsWritten);
+void SubstituteParameterMappings::buildParameterMapping(
+ NormalizedConstraintWithParamMapping &N) {
+ TemplateParameterList *TemplateParams =
+ cast<TemplateDecl>(N.getConstraintDecl())->getTemplateParameters();
+
+ llvm::SmallBitVector OccurringIndices(TemplateParams->size());
+ llvm::SmallBitVector OccurringIndicesForSubsumption(TemplateParams->size());
+
+ if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) {
+ SemaRef.MarkUsedTemplateParameters(
+ static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+ /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+
+ SemaRef.MarkUsedTemplateParametersForSubsumptionParameterMapping(
+ static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+ /*Depth=*/0, OccurringIndicesForSubsumption);
+
+ } else if (N.getKind() ==
+ NormalizedConstraint::ConstraintKind::FoldExpanded) {
+ SemaRef.MarkUsedTemplateParameters(
+ static_cast<FoldExpandedConstraint &>(N).getPattern(),
+ /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+ } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
+ auto *Args = static_cast<ConceptIdConstraint &>(N)
+ .getConceptId()
+ ->getTemplateArgsAsWritten();
+ if (Args)
+ SemaRef.MarkUsedTemplateParameters(Args->arguments(),
+ /*Depth=*/0, OccurringIndices);
}
+ TemplateArgumentLoc *TempArgs =
+ new (SemaRef.Context) TemplateArgumentLoc[OccurringIndices.count()];
+ llvm::SmallVector<NamedDecl *> UsedParams;
+ for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) {
+ SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
+ ? ArgsAsWritten->arguments()[I].getLocation()
+ : SourceLocation();
+ // FIXME: Investigate why we couldn't always preserve the SourceLoc. We
+ // can't assert Loc.isValid() now.
+ if (OccurringIndices[I]) {
+ NamedDecl *Param = TemplateParams->begin()[I];
+ new (&(TempArgs)[J]) TemplateArgumentLoc(
+ SemaRef.getIdentityTemplateArgumentLoc(Param, Loc));
+ UsedParams.push_back(Param);
+ J++;
+ }
+ }
+ auto *UsedList = TemplateParameterList::Create(
+ SemaRef.Context, TemplateParams->getTemplateLoc(),
+ TemplateParams->getLAngleLoc(), UsedParams,
+ /*RAngleLoc=*/SourceLocation(),
+ /*RequiresClause=*/nullptr);
+ unsigned Size = OccurringIndices.count();
+ N.updateParameterMapping(
+ std::move(OccurringIndices), std::move(OccurringIndicesForSubsumption),
+ MutableArrayRef<TemplateArgumentLoc>{TempArgs, Size}, UsedList);
+}
- TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
+bool SubstituteParameterMappings::substitute(
+ NormalizedConstraintWithParamMapping &N) {
+ if (!N.hasParameterMapping())
+ buildParameterMapping(N);
- AtomicConstraint &Atomic = *N.getAtomicConstraint();
- TemplateArgumentListInfo SubstArgs;
- if (!Atomic.ParameterMapping) {
- llvm::SmallBitVector OccurringIndices(TemplateParams->size());
- S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
- /*Depth=*/0, OccurringIndices);
- TemplateArgumentLoc *TempArgs =
- new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
- for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
- if (OccurringIndices[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 = ...;
- //
- // template<typename... Ts> requires C<Ts...>
- // struct S { };
- // The above currently yields a diagnostic.
- // We still might have default arguments for concept parameters.
- ArgsAsWritten->NumTemplateArgs > I
- ? ArgsAsWritten->arguments()[I].getLocation()
- : SourceLocation()));
- Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count());
- }
- SourceLocation InstLocBegin =
- ArgsAsWritten->arguments().empty()
- ? ArgsAsWritten->getLAngleLoc()
- : ArgsAsWritten->arguments().front().getSourceRange().getBegin();
- SourceLocation InstLocEnd =
- ArgsAsWritten->arguments().empty()
- ? ArgsAsWritten->getRAngleLoc()
- : ArgsAsWritten->arguments().front().getSourceRange().getEnd();
+ SourceLocation InstLocBegin, InstLocEnd;
+ llvm::ArrayRef Arguments = ArgsAsWritten->arguments();
+ if (Arguments.empty()) {
+ InstLocBegin = ArgsAsWritten->getLAngleLoc();
+ InstLocEnd = ArgsAsWritten->getRAngleLoc();
+ } else {
+ auto SR = Arguments[0].getSourceRange();
+ InstLocBegin = SR.getBegin();
+ InstLocEnd = SR.getEnd();
+ }
Sema::InstantiatingTemplate Inst(
- S, InstLocBegin,
+ SemaRef, InstLocBegin,
Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
- const_cast<NamedDecl *>(Atomic.ConstraintDecl),
+ const_cast<NamedDecl *>(N.getConstraintDecl()),
{InstLocBegin, InstLocEnd});
if (Inst.isInvalid())
return true;
- if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
+
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ TemplateArgumentListInfo SubstArgs;
+ if (SemaRef.SubstTemplateArgumentsInParameterMapping(
+ N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs,
+ /*BuildPackExpansionTypes=*/!InFoldExpr))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD =
+ const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl()));
+ if (SemaRef.CheckTemplateArgumentList(TD, N.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
return true;
TemplateArgumentLoc *TempArgs =
- new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
- std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
- TempArgs);
- Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
+ new (SemaRef.Context) TemplateArgumentLoc[CTAI.SugaredConverted.size()];
+
+ for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I) {
+ SourceLocation Loc;
+ // If this is an empty pack, we have no corresponding SubstArgs.
+ if (I < SubstArgs.size())
+ Loc = SubstArgs.arguments()[I].getLocation();
+
+ TempArgs[I] = SemaRef.getTrivialTemplateArgumentLoc(
+ CTAI.SugaredConverted[I], QualType(), Loc);
+ }
+
+ MutableArrayRef<TemplateArgumentLoc> Mapping(TempArgs,
+ CTAI.SugaredConverted.size());
+ N.updateParameterMapping(N.mappingOccurenceList(),
+ N.mappingOccurenceListForSubsumption(), Mapping,
+ N.getUsedTemplateParamList());
return false;
}
-static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- const ConceptSpecializationExpr *CSE) {
- MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
- CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
- /*Final=*/false, CSE->getTemplateArguments(),
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true);
+bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) {
+ assert(CC.getConstraintDecl() && MLTAL && ArgsAsWritten);
- return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
- CSE->getTemplateArgsAsWritten());
-}
+ if (substitute(static_cast<NormalizedConstraintWithParamMapping &>(CC)))
+ return true;
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- NormalizedConstraint LHS,
- NormalizedConstraint RHS,
- CompoundConstraintKind Kind)
- : Constraint{CompoundConstraint{
- new(C) NormalizedConstraintPair{std::move(LHS), std::move(RHS)},
- Kind}} {}
-
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- const NormalizedConstraint &Other) {
- if (Other.isAtomic()) {
- Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
- } else if (Other.isFoldExpanded()) {
- Constraint = new (C) FoldExpandedConstraint(
- Other.getFoldExpandedConstraint()->Kind,
- NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint),
- Other.getFoldExpandedConstraint()->Pattern);
+ auto *CSE = CC.getConceptSpecializationExpr();
+ assert(CSE);
+ assert(!CC.getBeginLoc().isInvalid());
+
+ SourceLocation InstLocBegin, InstLocEnd;
+ if (llvm::ArrayRef Arguments = ArgsAsWritten->arguments();
+ Arguments.empty()) {
+ InstLocBegin = ArgsAsWritten->getLAngleLoc();
+ InstLocEnd = ArgsAsWritten->getRAngleLoc();
} else {
- Constraint = CompoundConstraint(
- new (C)
- NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()),
- NormalizedConstraint(C, Other.getRHS())},
- Other.getCompoundKind());
+ auto SR = Arguments[0].getSourceRange();
+ InstLocBegin = SR.getBegin();
+ InstLocEnd = SR.getEnd();
}
-}
+ // This is useful for name lookup across modules; see Sema::getLookupModules.
+ Sema::InstantiatingTemplate Inst(
+ SemaRef, InstLocBegin,
+ Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+ const_cast<NamedDecl *>(CC.getConstraintDecl()),
+ {InstLocBegin, InstLocEnd});
+ if (Inst.isInvalid())
+ return true;
-NormalizedConstraint &NormalizedConstraint::getLHS() const {
- assert(isCompound() && "getLHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->LHS;
+ TemplateArgumentListInfo Out;
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ const ASTTemplateArgumentListInfo *ArgsAsWritten =
+ CSE->getTemplateArgsAsWritten();
+ if (SemaRef.SubstTemplateArgumentsInParameterMapping(
+ ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out,
+ /*BuildPackExpansionTypes=*/!InFoldExpr))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(),
+ CSE->getConceptNameInfo().getLoc(), Out,
+ /*DefaultArgs=*/{},
+ /*PartialTemplateArgs=*/false, CTAI,
+ /*UpdateArgsWithConversions=*/false))
+ return true;
+ auto TemplateArgs = *MLTAL;
+ TemplateArgs.replaceOutermostTemplateArguments(
+ TemplateArgs.getAssociatedDecl(0).first, CTAI.SugaredConverted);
+ return SubstituteParameterMappings(SemaRef, &TemplateArgs, ArgsAsWritten,
+ InFoldExpr)
+ .substitute(CC.getNormalizedConstraint());
}
-NormalizedConstraint &NormalizedConstraint::getRHS() const {
- assert(isCompound() && "getRHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->RHS;
+bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
+ switch (N.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic: {
+ if (!MLTAL) {
+ assert(!ArgsAsWritten);
+ return false;
+ }
+ return substitute(static_cast<NormalizedConstraintWithParamMapping &>(N));
+ }
+ case NormalizedConstraint::ConstraintKind::FoldExpanded: {
+ auto &FE = static_cast<FoldExpandedConstraint &>(N);
+ if (!MLTAL) {
+ llvm::SaveAndRestore _1(InFoldExpr, true);
+ assert(!ArgsAsWritten);
+ return substitute(FE.getNormalizedPattern());
+ }
+ Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt);
+ substitute(static_cast<NormalizedConstraintWithParamMapping &>(FE));
+ return SubstituteParameterMappings(SemaRef, /*InFoldExpr=*/true)
+ .substitute(FE.getNormalizedPattern());
+ }
+ case NormalizedConstraint::ConstraintKind::ConceptId: {
+ auto &CC = static_cast<ConceptIdConstraint &>(N);
+ if (MLTAL) {
+ assert(ArgsAsWritten);
+ return substitute(CC);
+ }
+ assert(!ArgsAsWritten);
+ const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr();
+ ConceptDecl *Concept = CSE->getNamedConcept();
+ MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs(
+ Concept, Concept->getLexicalDeclContext(),
+ /*Final=*/true, CSE->getTemplateArguments(),
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+
+ return SubstituteParameterMappings(
+ SemaRef, &MLTAL, CSE->getTemplateArgsAsWritten(), InFoldExpr)
+ .substitute(CC.getNormalizedConstraint());
+ }
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ auto &Compound = static_cast<CompoundConstraint &>(N);
+ if (substitute(Compound.getLHS()))
+ return true;
+ return substitute(Compound.getRHS());
+ }
+ }
}
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromAssociatedConstraints(
+} // namespace
+
+NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
assert(ACs.size() != 0);
- auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr);
+ auto *Conjunction =
+ fromConstraintExpr(S, D, ACs[0].ConstraintExpr, ACs[0].ArgPackSubstIndex);
if (!Conjunction)
- return std::nullopt;
+ return nullptr;
for (unsigned I = 1; I < ACs.size(); ++I) {
- auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr);
+ auto *Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr,
+ ACs[I].ArgPackSubstIndex);
if (!Next)
- return std::nullopt;
- *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction),
- std::move(*Next), CCK_Conjunction);
+ return nullptr;
+ Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(),
+ Conjunction, Next);
}
return Conjunction;
}
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
- const Expr *E) {
+NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
+ Sema &S, const NamedDecl *D, const Expr *E, UnsignedOrNone SubstIndex) {
assert(E != nullptr);
// C++ [temp.constr.normal]p1.1
@@ -1597,23 +2170,29 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// [...]
E = E->IgnoreParenImpCasts();
+ llvm::FoldingSetNodeID ID;
+ if (D && DiagRecursiveConstraintEval(S, ID, D, E)) {
+ return nullptr;
+ }
+ SatisfactionStackRAII StackRAII(S, D, ID);
+
// 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());
+ auto *LHS = fromConstraintExpr(S, D, BO.getLHS(), SubstIndex);
if (!LHS)
- return std::nullopt;
- auto RHS = fromConstraintExpr(S, D, BO.getRHS());
+ return nullptr;
+ auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex);
if (!RHS)
- return std::nullopt;
+ return nullptr;
- return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
- BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
+ return CompoundConstraint::Create(
+ S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS);
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
- const NormalizedConstraint *SubNF;
+ NormalizedConstraint *SubNF;
{
Sema::InstantiatingTemplate Inst(
S, CSE->getExprLoc(),
@@ -1621,7 +2200,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// FIXME: improve const-correctness of InstantiatingTemplate
const_cast<NamedDecl *>(D), CSE->getSourceRange());
if (Inst.isInvalid())
- return std::nullopt;
+ return nullptr;
// C++ [temp.constr.normal]p1.1
// [...]
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
@@ -1631,20 +2210,21 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// constraint. If any such substitution results in an invalid type or
// expression, the program is ill-formed; no diagnostic is required.
// [...]
- ConceptDecl *CD = CSE->getNamedConcept();
- SubNF = S.getNormalizedAssociatedConstraints(
- CD, AssociatedConstraint(CD->getConstraintExpr()));
+
+ // Use canonical declarations to merge ConceptDecls across
+ // different modules.
+ ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl();
+ SubNF = NormalizedConstraint::fromAssociatedConstraints(
+ S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
+
if (!SubNF)
- return std::nullopt;
+ return nullptr;
}
- std::optional<NormalizedConstraint> New;
- New.emplace(S.Context, *SubNF);
-
- if (substituteParameterMappings(S, *New, CSE))
- return std::nullopt;
+ return ConceptIdConstraint::Create(S.getASTContext(),
+ CSE->getConceptReference(), SubNF, D,
+ CSE, SubstIndex);
- return New;
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
FE && S.getLangOpts().CPlusPlus26 &&
(FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
@@ -1658,31 +2238,61 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
: FoldExpandedConstraint::FoldOperatorKind::Or;
if (FE->getInit()) {
- auto LHS = fromConstraintExpr(S, D, FE->getLHS());
- auto RHS = fromConstraintExpr(S, D, FE->getRHS());
+ auto *LHS = fromConstraintExpr(S, D, FE->getLHS(), SubstIndex);
+ auto *RHS = fromConstraintExpr(S, D, FE->getRHS(), SubstIndex);
if (!LHS || !RHS)
- return std::nullopt;
+ return nullptr;
if (FE->isRightFold())
- RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*RHS), FE->getPattern()}};
+ LHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), D, Kind, LHS);
else
- LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*LHS), FE->getPattern()}};
-
- return NormalizedConstraint(
- S.Context, std::move(*LHS), std::move(*RHS),
- FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
- : CCK_Disjunction);
+ RHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), D, Kind, RHS);
+
+ return CompoundConstraint::Create(
+ S.getASTContext(), LHS,
+ (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
+ : CCK_Disjunction),
+ RHS);
}
- auto Sub = fromConstraintExpr(S, D, FE->getPattern());
+ auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex);
if (!Sub)
- return std::nullopt;
- return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*Sub), FE->getPattern()}};
+ return nullptr;
+ return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(),
+ D, Kind, Sub);
}
+ return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex);
+}
- return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)};
+const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
+ ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
+ ArrayRef<AssociatedConstraint> AssociatedConstraints) {
+ if (!ConstrainedDeclOrNestedReq) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
+ *this, nullptr, AssociatedConstraints);
+ if (!Normalized ||
+ SubstituteParameterMappings(*this).substitute(*Normalized))
+ return nullptr;
+
+ return Normalized;
+ }
+
+ // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
+ const NamedDecl *ND =
+ ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+ auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
+ if (CacheEntry == NormalizationCache.end()) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
+ *this, ND, AssociatedConstraints);
+ CacheEntry =
+ NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
+ .first;
+ if (!Normalized ||
+ SubstituteParameterMappings(*this).substitute(*Normalized))
+ return nullptr;
+ }
+ return CacheEntry->second;
}
bool FoldExpandedConstraint::AreCompatibleForSubsumption(
@@ -1693,8 +2303,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
// if their respective constraints both contain an equivalent unexpanded pack.
llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks;
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks);
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.getPattern()),
+ APacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.getPattern()),
+ BPacks);
for (const UnexpandedParameterPack &APack : APacks) {
auto ADI = getDepthAndIndex(APack);
@@ -1788,7 +2400,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
const AtomicConstraint &B) {
if (!A.hasMatchingParameterMapping(Context, B))
return false;
- const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
+ const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr();
if (EA == EB)
return true;
@@ -1841,24 +2453,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
return true;
}
-NormalizedConstraint::CompoundConstraintKind
-NormalizedConstraint::getCompoundKind() const {
- assert(isCompound() && "getCompoundKind on a non-compound constraint..");
- return cast<CompoundConstraint>(Constraint).getInt();
-}
-
-AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const {
- assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint.");
- return cast<AtomicConstraint *>(Constraint);
-}
-
-FoldExpandedConstraint *
-NormalizedConstraint::getFoldExpandedConstraint() const {
- assert(isFoldExpanded() &&
- "getFoldExpandedConstraint called on non-fold-expanded constraint.");
- return cast<FoldExpandedConstraint *>(Constraint);
-}
-
//
//
// ------------------------ Subsumption -----------------------------------
@@ -1874,8 +2468,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() {
return NextID++;
}
-auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
- auto &Elems = AtomicMap[Ori->ConstraintExpr];
+auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal {
+ auto &Elems = AtomicMap[Ori->getConstraintExpr()];
// C++ [temp.constr.order] p2
// - an atomic constraint A subsumes another atomic constraint B
// if and only if the A and B are identical [...]
@@ -1891,13 +2485,16 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
// subsumes another, their literal will be the same
llvm::FoldingSetNodeID ID;
- const auto &Mapping = Ori->ParameterMapping;
- ID.AddBoolean(Mapping.has_value());
- if (Mapping) {
- for (const TemplateArgumentLoc &TAL : *Mapping) {
- SemaRef.getASTContext()
- .getCanonicalTemplateArgument(TAL.getArgument())
- .Profile(ID, SemaRef.getASTContext());
+ ID.AddBoolean(Ori->hasParameterMapping());
+ if (Ori->hasParameterMapping()) {
+ const auto &Mapping = Ori->getParameterMapping();
+ const NormalizedConstraint::OccurenceList &Indexes =
+ Ori->mappingOccurenceListForSubsumption();
+ for (auto [Idx, TAL] : llvm::enumerate(Mapping)) {
+ if (Indexes[Idx])
+ SemaRef.getASTContext()
+ .getCanonicalTemplateArgument(TAL.getArgument())
+ .Profile(ID, SemaRef.getASTContext());
}
}
auto It = Elems.find(ID);
@@ -1912,11 +2509,11 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
return It->getSecond().ID;
}
-auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal {
- auto &Elems = FoldMap[Ori->Pattern];
+auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal {
+ auto &Elems = FoldMap[Ori->getPattern()];
FoldExpendedConstraintKey K;
- K.Kind = Ori->Kind;
+ K.Kind = Ori->getFoldOperator();
auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) {
return K.Kind == Other.Kind;
@@ -1960,38 +2557,47 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) {
AddUniqueClauseToFormula(Res, std::move(C));
};
- if (NC.isAtomic())
- return {{find(NC.getAtomicConstraint())}};
+ switch (NC.getKind()) {
- if (NC.isFoldExpanded())
- return {{find(NC.getFoldExpandedConstraint())}};
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return {{find(&static_cast<const AtomicConstraint &>(NC))}};
- FormulaType Left, Right;
- SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
- Left = Normalize<FormulaType>(NC.getLHS());
- Right = Normalize<FormulaType>(NC.getRHS());
- });
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return {{find(&static_cast<const FoldExpandedConstraint &>(NC))}};
- if (NC.getCompoundKind() == FormulaType::Kind) {
- auto SizeLeft = Left.size();
- Res = std::move(Left);
- Res.reserve(SizeLeft + Right.size());
- std::for_each(std::make_move_iterator(Right.begin()),
- std::make_move_iterator(Right.end()), Add);
- return Res;
- }
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return Normalize<FormulaType>(
+ static_cast<const ConceptIdConstraint &>(NC).getNormalizedConstraint());
+
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ const auto &Compound = static_cast<const CompoundConstraint &>(NC);
+ FormulaType Left, Right;
+ SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
+ Left = Normalize<FormulaType>(Compound.getLHS());
+ Right = Normalize<FormulaType>(Compound.getRHS());
+ });
+
+ if (Compound.getCompoundKind() == FormulaType::Kind) {
+ Res = std::move(Left);
+ Res.reserve(Left.size() + Right.size());
+ std::for_each(std::make_move_iterator(Right.begin()),
+ std::make_move_iterator(Right.end()), Add);
+ return Res;
+ }
- Res.reserve(Left.size() * Right.size());
- for (const auto &LTransform : Left) {
- for (const auto &RTransform : Right) {
- Clause Combined;
- Combined.reserve(LTransform.size() + RTransform.size());
- llvm::append_range(Combined, LTransform);
- llvm::append_range(Combined, RTransform);
- Add(std::move(Combined));
+ Res.reserve(Left.size() * Right.size());
+ for (const auto &LTransform : Left) {
+ for (const auto &RTransform : Right) {
+ Clause Combined;
+ Combined.reserve(LTransform.size() + RTransform.size());
+ llvm::copy(LTransform, std::back_inserter(Combined));
+ llvm::copy(RTransform, std::back_inserter(Combined));
+ Add(std::move(Combined));
+ }
}
+ return Res;
+ }
}
- return Res;
}
void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
@@ -2006,12 +2612,12 @@ std::optional<bool> SubsumptionChecker::Subsumes(
const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
ArrayRef<AssociatedConstraint> Q) {
const NormalizedConstraint *PNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DP, P);
+ SemaRef.getNormalizedAssociatedConstraints(DP, P);
if (!PNormalized)
return std::nullopt;
const NormalizedConstraint *QNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DQ, Q);
+ SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
if (!QNormalized)
return std::nullopt;
@@ -2061,9 +2667,9 @@ bool SubsumptionChecker::Subsumes(const FoldExpandedConstraint *A,
// constraint B if they are compatible for subsumption, have the same
// fold-operator, and the constraint of A subsumes that of B.
bool DoesSubsume =
- A->Kind == B->Kind &&
+ A->getFoldOperator() == B->getFoldOperator() &&
FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) &&
- Subsumes(&A->Constraint, &B->Constraint);
+ Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern());
It = FoldSubsumptionCache.try_emplace(std::move(Key), DoesSubsume).first;
}
return It->second;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 16d42d2..d27f767 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17876,13 +17876,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
findFailedBooleanCondition(Converted.get());
if (const auto *ConceptIDExpr =
dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) {
- // Drill down into concept specialization expressions to see why they
- // weren't satisfied.
- Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
- << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
- ConstraintSatisfaction Satisfaction;
- if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction))
- DiagnoseUnsatisfiedConstraint(Satisfaction);
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConceptIDExpr->getSatisfaction();
+ if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) {
+ Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
+ << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
+ // Drill down into concept specialization expressions to see why they
+ // weren't satisfied.
+ DiagnoseUnsatisfiedConstraint(ConceptIDExpr);
+ }
} else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) &&
!isa<IntegerLiteral>(InnerCond)) {
Diag(InnerCond->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 576eb32..0fe242dce 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7935,21 +7935,27 @@ Sema::BuildExprRequirement(
// be satisfied.
TemplateParameterList *TPL =
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
- QualType MatchedType =
- Context.getReferenceQualifiedType(E).getCanonicalType();
+ QualType MatchedType = Context.getReferenceQualifiedType(E);
llvm::SmallVector<TemplateArgument, 1> Args;
Args.push_back(TemplateArgument(MatchedType));
auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));
- MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false);
+ MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true);
MLTAL.addOuterRetainedLevels(TPL->getDepth());
const TypeConstraint *TC = Param->getTypeConstraint();
assert(TC && "Type Constraint cannot be null here");
auto *IDC = TC->getImmediatelyDeclaredConstraint();
assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
ExprResult Constraint = SubstExpr(IDC, MLTAL);
- if (Constraint.isInvalid()) {
+ bool HasError = Constraint.isInvalid();
+ if (!HasError) {
+ SubstitutedConstraintExpr =
+ cast<ConceptSpecializationExpr>(Constraint.get());
+ if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors)
+ HasError = true;
+ }
+ if (HasError) {
return new (Context) concepts::ExprRequirement(
createSubstDiagAt(IDC->getExprLoc(),
[&](llvm::raw_ostream &OS) {
@@ -7958,8 +7964,6 @@ Sema::BuildExprRequirement(
}),
IsSimple, NoexceptLoc, ReturnTypeRequirement);
}
- SubstitutedConstraintExpr =
- cast<ConceptSpecializationExpr>(Constraint.get());
if (!SubstitutedConstraintExpr->isSatisfied())
Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index c971293..0d0d2c0 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8219,8 +8219,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
// InitializeTemporary entity for our target type.
QualType Ty = Step->Type;
bool IsTemporary = !S.Context.hasSameType(Entity.getType(), Ty);
- InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(Ty);
- InitializedEntity InitEntity = IsTemporary ? TempEntity : Entity;
+ InitializedEntity InitEntity =
+ IsTemporary ? InitializedEntity::InitializeTemporary(Ty) : Entity;
InitListChecker PerformInitList(S, InitEntity,
InitList, Ty, /*VerifyOnly=*/false,
/*TreatUnavailableAsInvalid=*/false);
@@ -8242,7 +8242,6 @@ ExprResult InitializationSequence::Perform(Sema &S,
InitListExpr *StructuredInitList =
PerformInitList.getFullyStructuredList();
- CurInit.get();
CurInit = shouldBindAsTemporary(InitEntity)
? S.MaybeBindToTemporary(StructuredInitList)
: StructuredInitList;
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 9aaf7f4..7ad7049 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -2894,17 +2894,18 @@ SemaOpenACC::CreateFirstPrivateInitRecipe(const Expr *VarExpr) {
OpenACCReductionRecipe SemaOpenACC::CreateReductionInitRecipe(
OpenACCReductionOperator ReductionOperator, const Expr *VarExpr) {
- // TODO: OpenACC: This shouldn't be necessary, see PrivateInitRecipe
- VarExpr = StripOffBounds(VarExpr);
-
+ // We don't strip bounds here, so that we are doing our recipe init at the
+ // 'lowest' possible level. Codegen is going to have to do its own 'looping'.
if (!VarExpr || VarExpr->getType()->isDependentType())
return OpenACCReductionRecipe::Empty();
QualType VarTy =
VarExpr->getType().getNonReferenceType().getUnqualifiedType();
- // TODO: OpenACC: for arrays/bounds versions, we're going to have to do a
- // different initializer, but for now we can go ahead with this.
+ // Array sections are special, and we have to treat them that way.
+ if (const auto *ASE =
+ dyn_cast<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts()))
+ VarTy = ArraySectionExpr::getBaseOriginalType(ASE);
VarDecl *AllocaDecl = CreateAllocaDecl(
getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ea5c4265..b870114 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -804,7 +804,7 @@ clang::MakeDeductionFailureInfo(ASTContext &Context,
case TemplateDeductionResult::ConstraintsNotSatisfied: {
CNSInfo *Saved = new (Context) CNSInfo;
Saved->TemplateArgs = Info.takeSugared();
- Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction;
+ Saved->Satisfaction = std::move(Info.AssociatedConstraintsSatisfaction);
Result.Data = Saved;
break;
}
@@ -852,6 +852,7 @@ void DeductionFailureInfo::Destroy() {
case TemplateDeductionResult::ConstraintsNotSatisfied:
// FIXME: Destroy the template argument list?
+ static_cast<CNSInfo *>(Data)->Satisfaction.~ConstraintSatisfaction();
Data = nullptr;
if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
Diag->~PartialDiagnosticAt();
@@ -12739,7 +12740,8 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
<< FnDesc /* Ignored */;
ConstraintSatisfaction Satisfaction;
- if (S.CheckFunctionConstraints(Fn, Satisfaction))
+ if (S.CheckFunctionConstraints(Fn, Satisfaction, SourceLocation(),
+ /*ForOverloadResolution=*/true))
break;
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 2bf1511..dcf2876 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9,6 +9,7 @@
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -1222,8 +1223,9 @@ static ExprResult formImmediatelyDeclaredConstraint(
if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
- /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
- &ConstraintArgs);
+ /*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs,
+ /*DoCheckConstraintSatisfaction=*/
+ !S.inParameterMappingSubstitution());
}
// We have a template template parameter
else {
@@ -4850,13 +4852,11 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
diagnoseMissingTemplateArguments(Name, Loc);
}
-ExprResult
-Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
- SourceLocation TemplateKWLoc,
- const DeclarationNameInfo &ConceptNameInfo,
- NamedDecl *FoundDecl,
- ConceptDecl *NamedConcept,
- const TemplateArgumentListInfo *TemplateArgs) {
+ExprResult Sema::CheckConceptTemplateId(
+ const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl,
+ TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
+ bool DoCheckConstraintSatisfaction) {
assert(NamedConcept && "A concept template id without a template?");
if (NamedConcept->isInvalidDecl())
@@ -4873,33 +4873,48 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc());
+ // There's a bug with CTAI.CanonicalConverted.
+ // If the template argument contains a DependentDecltypeType that includes a
+ // TypeAliasType, and the same written type had occurred previously in the
+ // source, then the DependentDecltypeType would be canonicalized to that
+ // previous type which would mess up the substitution.
+ // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly!
auto *CSD = ImplicitConceptSpecializationDecl::Create(
Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(),
- CTAI.CanonicalConverted);
+ CTAI.SugaredConverted);
ConstraintSatisfaction Satisfaction;
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(
- *TemplateArgs, CTAI.CanonicalConverted);
- MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted,
+ *TemplateArgs, CTAI.SugaredConverted);
+ MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.SugaredConverted,
/*Final=*/false);
- LocalInstantiationScope Scope(*this);
-
- EnterExpressionEvaluationContext EECtx{
- *this, ExpressionEvaluationContext::Unevaluated, CSD};
-
- if (!AreArgsDependent &&
- CheckConstraintSatisfaction(
- NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
- MLTAL,
- SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
- TemplateArgs->getRAngleLoc()),
- Satisfaction))
- return ExprError();
auto *CL = ConceptReference::Create(
Context,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs));
+
+ bool Error = false;
+ if (const auto *Concept = dyn_cast<ConceptDecl>(NamedConcept);
+ Concept && Concept->getConstraintExpr() && !AreArgsDependent &&
+ DoCheckConstraintSatisfaction) {
+
+ LocalInstantiationScope Scope(*this);
+
+ EnterExpressionEvaluationContext EECtx{
+ *this, ExpressionEvaluationContext::Unevaluated, CSD};
+
+ Error = CheckConstraintSatisfaction(
+ NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
+ SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
+ TemplateArgs->getRAngleLoc()),
+ Satisfaction, CL);
+ Satisfaction.ContainsErrors = Error;
+ }
+
+ if (Error)
+ return ExprError();
+
return ConceptSpecializationExpr::Create(
Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
}
@@ -5217,10 +5232,11 @@ bool Sema::CheckTemplateTypeArgument(
}
default: {
// We allow instantiating a template with template argument packs when
- // building deduction guides.
+ // building deduction guides or mapping constraint template parameters.
if (Arg.getKind() == TemplateArgument::Pack &&
- CodeSynthesisContexts.back().Kind ==
- Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ (CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides ||
+ inParameterMappingSubstitution())) {
SugaredConverted.push_back(Arg);
CanonicalConverted.push_back(Arg);
return false;
@@ -5813,6 +5829,20 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgumentListInfo &TemplateArgs, const DefaultArguments &DefaultArgs,
bool PartialTemplateArgs, CheckTemplateArgumentInfo &CTAI,
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) {
+ return CheckTemplateArgumentList(
+ Template, GetTemplateParameterList(Template), TemplateLoc, TemplateArgs,
+ DefaultArgs, PartialTemplateArgs, CTAI, UpdateArgsWithConversions,
+ ConstraintsNotSatisfied);
+}
+
+/// Check that the given template argument list is well-formed
+/// for specializing the given template.
+bool Sema::CheckTemplateArgumentList(
+ TemplateDecl *Template, TemplateParameterList *Params,
+ SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs,
+ const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
+ CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions,
+ bool *ConstraintsNotSatisfied) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = false;
@@ -5822,8 +5852,6 @@ bool Sema::CheckTemplateArgumentList(
// template.
TemplateArgumentListInfo NewArgs = TemplateArgs;
- TemplateParameterList *Params = GetTemplateParameterList(Template);
-
SourceLocation RAngleLoc = NewArgs.getRAngleLoc();
// C++23 [temp.arg.general]p1:
@@ -6163,11 +6191,12 @@ bool Sema::CheckTemplateArgumentList(
CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr);
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
- Template, NewContext, /*Final=*/false, CTAI.CanonicalConverted,
+ Template, NewContext, /*Final=*/true, CTAI.SugaredConverted,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
- if (EnsureTemplateArgumentListConstraints(
+ if (!isa<ConceptDecl>(Template) &&
+ EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
if (ConstraintsNotSatisfied)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f6ee745..6bba505 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3206,7 +3206,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// If we don't need to replace the deduced template arguments,
// we can add them immediately as the inner-most argument list.
if (!DeducedArgsNeedReplacement)
- Innermost = CanonicalDeducedArgs;
+ Innermost = SugaredDeducedArgs;
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
Template, Template->getDeclContext(), /*Final=*/false, Innermost,
@@ -3218,7 +3218,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (!Innermost)
- MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
+ MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
@@ -3995,11 +3995,12 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
if (CheckFunctionTemplateConstraints(
Info.getLocation(),
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
- CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
+ CTAI.SugaredConverted, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
- Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
- Context, CTAI.CanonicalConverted));
+ Info.reset(
+ TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted),
+ Info.takeCanonical());
return TemplateDeductionResult::ConstraintsNotSatisfied;
}
}
@@ -5167,8 +5168,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
/*DefaultArgs=*/{},
/*PartialTemplateArgs=*/false, CTAI))
return true;
- MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.CanonicalConverted,
- /*Final=*/false);
+ MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted,
+ /*Final=*/true);
// Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so
// that the template arguments of the constraint can be preserved. For
// example:
@@ -5182,7 +5183,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
S, Sema::ExpressionEvaluationContext::Unevaluated,
ImplicitConceptSpecializationDecl::Create(
S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(),
- CTAI.CanonicalConverted));
+ CTAI.SugaredConverted));
if (S.CheckConstraintSatisfaction(
Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
TypeLoc.getLocalSourceRange(), Satisfaction))
@@ -6676,10 +6677,11 @@ namespace {
struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
llvm::SmallBitVector &Used;
unsigned Depth;
+ bool VisitDeclRefTypes = true;
- MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used,
- unsigned Depth)
- : Used(Used), Depth(Depth) { }
+ MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, unsigned Depth,
+ bool VisitDeclRefTypes = true)
+ : Used(Used), Depth(Depth), VisitDeclRefTypes(VisitDeclRefTypes) {}
bool VisitTemplateTypeParmType(TemplateTypeParmType *T) override {
if (T->getDepth() == Depth)
@@ -6700,6 +6702,8 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(E->getDecl()))
if (NTTP->getDepth() == Depth)
Used[NTTP->getIndex()] = true;
+ if (VisitDeclRefTypes)
+ DynamicRecursiveASTVisitor::TraverseType(E->getType());
return true;
}
@@ -7043,10 +7047,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
break;
case Type::UnaryTransform:
- if (!OnlyDeduced)
- MarkUsedTemplateParameters(Ctx,
- cast<UnaryTransformType>(T)->getUnderlyingType(),
- OnlyDeduced, Depth, Used);
+ if (!OnlyDeduced) {
+ auto *UTT = cast<UnaryTransformType>(T);
+ auto Next = UTT->getUnderlyingType();
+ if (Next.isNull())
+ Next = UTT->getBaseType();
+ MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used);
+ }
break;
case Type::PackExpansion:
@@ -7146,6 +7153,12 @@ Sema::MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
::MarkUsedTemplateParameters(Context, E, OnlyDeduced, Depth, Used);
}
+void Sema::MarkUsedTemplateParametersForSubsumptionParameterMapping(
+ const Expr *E, unsigned Depth, llvm::SmallBitVector &Used) {
+ MarkUsedTemplateParameterVisitor(Used, Depth, /*VisitDeclRefTypes=*/false)
+ .TraverseStmt(const_cast<Expr *>(E));
+}
+
void
Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
bool OnlyDeduced, unsigned Depth,
@@ -7171,6 +7184,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
/*OnlyDeduced=*/false, Depth, Used);
}
+void Sema::MarkUsedTemplateParameters(
+ ArrayRef<TemplateArgumentLoc> TemplateArgs, unsigned Depth,
+ llvm::SmallBitVector &Used) {
+ for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
+ ::MarkUsedTemplateParameters(Context, TemplateArgs[I].getArgument(),
+ /*OnlyDeduced=*/false, Depth, Used);
+}
+
void Sema::MarkDeducedTemplateParameters(
ASTContext &Ctx, const FunctionTemplateDecl *FunctionTemplate,
llvm::SmallBitVector &Deduced) {
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index fe673ea..9a61888 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -1171,17 +1171,46 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Args.addOuterTemplateArguments(TransformedDeducedAliasArgs);
for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
const auto &D = DeduceResults[Index];
+ auto *TP = F->getTemplateParameters()->getParam(Index);
if (IsNonDeducedArgument(D)) {
// 2): Non-deduced template parameters would be substituted later.
continue;
}
TemplateArgumentLoc Input =
SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
- TemplateArgumentLoc Output;
- if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) {
- assert(TemplateArgsForBuildingFPrime[Index].isNull() &&
- "InstantiatedArgs must be null before setting");
- TemplateArgsForBuildingFPrime[Index] = Output.getArgument();
+ TemplateArgumentListInfo Output;
+ if (SemaRef.SubstTemplateArguments(Input, Args, Output))
+ return nullptr;
+ assert(TemplateArgsForBuildingFPrime[Index].isNull() &&
+ "InstantiatedArgs must be null before setting");
+ // CheckTemplateArgument is necessary for NTTP initializations.
+ // FIXME: We may want to call CheckTemplateArguments instead, but we cannot
+ // match packs as usual, since packs can appear in the middle of the
+ // parameter list of a synthesized CTAD guide. See also the FIXME in
+ // test/SemaCXX/cxx20-ctad-type-alias.cpp:test25.
+ Sema::CheckTemplateArgumentInfo CTAI;
+ if (Input.getArgument().getKind() == TemplateArgument::Pack) {
+ for (auto TA : Output.arguments()) {
+ if (SemaRef.CheckTemplateArgument(
+ TP, TA, F, F->getLocation(), F->getLocation(),
+ /*ArgumentPackIndex=*/-1, CTAI,
+ Sema::CheckTemplateArgumentKind::CTAK_Specified))
+ return nullptr;
+ }
+ // We will substitute the non-deduced template arguments with these
+ // transformed (unpacked at this point) arguments, where that substitution
+ // requires a pack for the corresponding parameter packs.
+ TemplateArgsForBuildingFPrime[Index] =
+ TemplateArgument::CreatePackCopy(Context, CTAI.SugaredConverted);
+ } else {
+ assert(Output.arguments().size() == 1);
+ TemplateArgumentLoc Transformed = Output.arguments()[0];
+ if (SemaRef.CheckTemplateArgument(
+ TP, Transformed, F, F->getLocation(), F->getLocation(),
+ /*ArgumentPackIndex=*/-1, CTAI,
+ Sema::CheckTemplateArgumentKind::CTAK_Specified))
+ return nullptr;
+ TemplateArgsForBuildingFPrime[Index] = CTAI.SugaredConverted[0];
}
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index f1c9c5c..1f762ca 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -628,9 +628,14 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
Inst.InstantiationRange = InstantiationRange;
Inst.InConstraintSubstitution =
Inst.Kind == CodeSynthesisContext::ConstraintSubstitution;
- if (!SemaRef.CodeSynthesisContexts.empty())
+ Inst.InParameterMappingSubstitution =
+ Inst.Kind == CodeSynthesisContext::ParameterMappingSubstitution;
+ if (!SemaRef.CodeSynthesisContexts.empty()) {
Inst.InConstraintSubstitution |=
SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution;
+ Inst.InParameterMappingSubstitution |=
+ SemaRef.CodeSynthesisContexts.back().InParameterMappingSubstitution;
+ }
Invalid = SemaRef.pushCodeSynthesisContext(Inst);
if (!Invalid) {
@@ -1375,6 +1380,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
// Template Instantiation for Types
//===----------------------------------------------------------------------===/
namespace {
+
class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
@@ -1387,7 +1393,11 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
- private:
+ // Whether to rebuild pack expansion types; We don't do that when
+ // rebuilding the parameter mapping of a fold expression appearing
+ // in a constraint expression.
+ bool BuildPackExpansionTypes = true;
+
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
@@ -1410,6 +1420,17 @@ namespace {
return EvaluateConstraints;
}
+ inline static struct ForParameterMappingSubstitution_t {
+ } ForParameterMappingSubstitution;
+
+ TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
+ SourceLocation Loc,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool BuildPackExpansionTypes)
+ : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
+ BailOutOnIncomplete(false),
+ BuildPackExpansionTypes(BuildPackExpansionTypes) {}
+
/// Determine whether the given type \p T has already been
/// transformed.
///
@@ -1444,7 +1465,8 @@ namespace {
bool &ShouldExpand, bool &RetainExpansion,
UnsignedOrNone &NumExpansions) {
if (SemaRef.CurrentInstantiationScope &&
- SemaRef.inConstraintSubstitution()) {
+ (SemaRef.inConstraintSubstitution() ||
+ SemaRef.inParameterMappingSubstitution())) {
for (UnexpandedParameterPack ParmPack : Unexpanded) {
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
@@ -1465,10 +1487,10 @@ namespace {
TemplateArgument ForgetPartiallySubstitutedPack() {
TemplateArgument Result;
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope
+ ->getPartiallySubstitutedPack()) {
+ MultiLevelTemplateArgumentList &TemplateArgs =
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
unsigned Depth, Index;
std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
@@ -1488,10 +1510,10 @@ namespace {
if (Arg.isNull())
return;
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope
+ ->getPartiallySubstitutedPack()) {
+ MultiLevelTemplateArgumentList &TemplateArgs =
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
unsigned Depth, Index;
std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
TemplateArgs.setArgument(Depth, Index, Arg);
@@ -1508,9 +1530,9 @@ namespace {
std::move(New);
return Old;
}
+
void RememberSubstitution(MultiLevelTemplateArgumentList Old) {
- const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) =
- std::move(Old);
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) = Old;
}
TemplateArgument
@@ -1691,6 +1713,24 @@ namespace {
return inherited::TransformTemplateArgument(Input, Output, Uneval);
}
+ // This has to be here to allow its overload.
+ ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
+ UnsignedOrNone NumExpansions) {
+ return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
+ NumExpansions);
+ }
+
+ TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern,
+ SourceLocation EllipsisLoc,
+ UnsignedOrNone NumExpansions) {
+ // We don't rewrite a PackExpansion type when we want to normalize a
+ // CXXFoldExpr constraint. We'll expand it when evaluating the constraint.
+ if (BuildPackExpansionTypes)
+ return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
+ NumExpansions);
+ return Pattern;
+ }
+
using TreeTransform::TransformTemplateSpecializationType;
QualType
TransformTemplateSpecializationType(TypeLocBuilder &TLB,
@@ -1961,7 +2001,8 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
PVD && SemaRef.CurrentInstantiationScope &&
- SemaRef.inConstraintSubstitution() &&
+ (SemaRef.inConstraintSubstitution() ||
+ SemaRef.inParameterMappingSubstitution()) &&
maybeInstantiateFunctionParameterToScope(PVD))
return nullptr;
@@ -2759,18 +2800,29 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
concepts::NestedRequirement *
TemplateInstantiator::TransformNestedRequirement(
concepts::NestedRequirement *Req) {
- if (!Req->isDependent() && !AlwaysRebuild())
- return Req;
+
+ ASTContext &C = SemaRef.Context;
+
+ Expr *Constraint = Req->getConstraintExpr();
+ ConstraintSatisfaction Satisfaction;
+
+ auto NestedReqWithDiag = [&C, this](Expr *E,
+ ConstraintSatisfaction Satisfaction) {
+ Satisfaction.IsSatisfied = false;
+ SmallString<128> Entity;
+ llvm::raw_svector_ostream OS(Entity);
+ E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
+ return new (C) concepts::NestedRequirement(
+ SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction));
+ };
+
if (Req->hasInvalidConstraint()) {
if (AlwaysRebuild())
return RebuildNestedRequirement(Req->getInvalidConstraintEntity(),
Req->getConstraintSatisfaction());
return Req;
}
- Sema::InstantiatingTemplate ReqInst(SemaRef,
- Req->getConstraintExpr()->getBeginLoc(), Req,
- Sema::InstantiatingTemplate::ConstraintsCheck{},
- Req->getConstraintExpr()->getSourceRange());
+
if (!getEvaluateConstraints()) {
ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
if (TransConstraint.isInvalid() || !TransConstraint.get())
@@ -2783,45 +2835,45 @@ TemplateInstantiator::TransformNestedRequirement(
SemaRef.Context, TransConstraint.get(), Satisfaction);
}
- ExprResult TransConstraint;
- ConstraintSatisfaction Satisfaction;
- TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc());
+ bool Success;
+ Expr *NewConstraint;
+ TemplateDeductionInfo Info(Constraint->getBeginLoc());
{
EnterExpressionEvaluationContext ContextRAII(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- Sema::SFINAETrap Trap(SemaRef);
- Sema::InstantiatingTemplate ConstrInst(SemaRef,
- Req->getConstraintExpr()->getBeginLoc(), Req, Info,
- Req->getConstraintExpr()->getSourceRange());
+
+ Sema::InstantiatingTemplate ConstrInst(
+ SemaRef, Constraint->getBeginLoc(), Req,
+ Sema::InstantiatingTemplate::ConstraintsCheck(),
+ Constraint->getSourceRange());
+
if (ConstrInst.isInvalid())
return nullptr;
- llvm::SmallVector<Expr *> Result;
- if (!SemaRef.CheckConstraintSatisfaction(
- nullptr,
- AssociatedConstraint(Req->getConstraintExpr(),
- SemaRef.ArgPackSubstIndex),
- Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(),
- Satisfaction) &&
- !Result.empty())
- TransConstraint = Result[0];
- assert(!Trap.hasErrorOccurred() && "Substitution failures must be handled "
- "by CheckConstraintSatisfaction.");
+
+ Sema::SFINAETrap Trap(SemaRef);
+
+ Success = !SemaRef.CheckConstraintSatisfaction(
+ Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
+ TemplateArgs, Constraint->getSourceRange(), Satisfaction,
+ /*TopLevelConceptId=*/nullptr, &NewConstraint);
+
+ assert(!Success || !Trap.hasErrorOccurred() &&
+ "Substitution failures must be handled "
+ "by CheckConstraintSatisfaction.");
}
- ASTContext &C = SemaRef.Context;
- if (TransConstraint.isUsable() &&
- TransConstraint.get()->isInstantiationDependent())
- return new (C) concepts::NestedRequirement(TransConstraint.get());
- if (TransConstraint.isInvalid() || !TransConstraint.get() ||
- Satisfaction.HasSubstitutionFailure()) {
- SmallString<128> Entity;
- llvm::raw_svector_ostream OS(Entity);
- Req->getConstraintExpr()->printPretty(OS, nullptr,
- SemaRef.getPrintingPolicy());
- return new (C) concepts::NestedRequirement(
- SemaRef.Context, C.backupStr(Entity), Satisfaction);
+
+ if (!Success || Satisfaction.HasSubstitutionFailure())
+ return NestedReqWithDiag(Constraint, Satisfaction);
+
+ // FIXME: const correctness
+ // MLTAL might be dependent.
+ if (!NewConstraint) {
+ if (!Satisfaction.IsSatisfied)
+ return NestedReqWithDiag(Constraint, Satisfaction);
+
+ NewConstraint = Constraint;
}
- return new (C)
- concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction);
+ return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction);
}
TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
@@ -3078,7 +3130,7 @@ bool Sema::SubstTypeConstraint(
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
- if (!EvaluateConstraints) {
+ if (!EvaluateConstraints && !inParameterMappingSubstitution()) {
UnsignedOrNone Index = TC->getArgPackSubstIndex();
if (!Index)
Index = SemaRef.ArgPackSubstIndex;
@@ -4378,6 +4430,16 @@ bool Sema::SubstTemplateArguments(
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
+bool Sema::SubstTemplateArgumentsInParameterMapping(
+ ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes) {
+ TemplateInstantiator Instantiator(
+ TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc,
+ TemplateArgs, BuildPackExpansionTypes);
+ return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
+}
+
ExprResult
Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!E)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 6967301..51b55b8 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3722,10 +3722,6 @@ public:
ParentContext);
}
- /// Build a new Objective-C boxed expression.
- ///
- /// By default, performs semantic analysis to build the new expression.
- /// Subclasses may override this routine to provide different behavior.
ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
@@ -5110,9 +5106,13 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
+
+ TemplateArgumentListInfo *PackOutput = &Outputs;
+ TemplateArgumentListInfo New;
+
if (TransformTemplateArguments(
PackLocIterator(*this, In.getArgument().pack_begin()),
- PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+ PackLocIterator(*this, In.getArgument().pack_end()), *PackOutput,
Uneval))
return true;
@@ -5179,7 +5179,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
return false;
-
}
// FIXME: Find ways to reduce code duplication for pack expansions.
@@ -6247,7 +6246,7 @@ ParmVarDecl *TreeTransform<Derived>::TransformFunctionTypeParam(
/* DefArg */ nullptr);
newParm->setScopeInfo(OldParm->getFunctionScopeDepth(),
OldParm->getFunctionScopeIndex() + indexAdjustment);
- transformedLocalDecl(OldParm, {newParm});
+ getDerived().transformedLocalDecl(OldParm, {newParm});
return newParm;
}
@@ -7082,11 +7081,11 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
TypeLocBuilder &TLB,
UnaryTransformTypeLoc TL) {
QualType Result = TL.getType();
+ TypeSourceInfo *NewBaseTSI = TL.getUnderlyingTInfo();
if (Result->isDependentType()) {
const UnaryTransformType *T = TL.getTypePtr();
- TypeSourceInfo *NewBaseTSI =
- getDerived().TransformType(TL.getUnderlyingTInfo());
+ NewBaseTSI = getDerived().TransformType(TL.getUnderlyingTInfo());
if (!NewBaseTSI)
return QualType();
QualType NewBase = NewBaseTSI->getType();
@@ -7101,7 +7100,7 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
UnaryTransformTypeLoc NewTL = TLB.push<UnaryTransformTypeLoc>(Result);
NewTL.setKWLoc(TL.getKWLoc());
NewTL.setParensRange(TL.getParensRange());
- NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo());
+ NewTL.setUnderlyingTInfo(NewBaseTSI);
return Result;
}
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index cf32d4f..5456e73 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2424,7 +2424,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
VisitDecl(D);
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
- Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true));
+ Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
D->setTemplateArguments(Args);
}
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 70b898a..eef97a8 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -807,15 +807,19 @@ readConstraintSatisfaction(ASTRecordReader &Record) {
if (!Satisfaction.IsSatisfied) {
unsigned NumDetailRecords = Record.readInt();
for (unsigned i = 0; i != NumDetailRecords; ++i) {
- if (/* IsDiagnostic */Record.readInt()) {
+ auto Kind = Record.readInt();
+ if (Kind == 0) {
SourceLocation DiagLocation = Record.readSourceLocation();
StringRef DiagMessage = C.backupStr(Record.readString());
- Satisfaction.Details.emplace_back(
- new (C) ConstraintSatisfaction::SubstitutionDiagnostic(
- DiagLocation, DiagMessage));
- } else
+ Satisfaction.Details.emplace_back(new (
+ C) ConstraintSubstitutionDiagnostic(DiagLocation, DiagMessage));
+ } else if (Kind == 1) {
Satisfaction.Details.emplace_back(Record.readExpr());
+ } else {
+ assert(Kind == 2);
+ Satisfaction.Details.emplace_back(Record.readConceptReference());
+ }
}
}
return Satisfaction;
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index ebda91e..acf3453 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -482,14 +482,20 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
if (!Satisfaction.IsSatisfied) {
Record.push_back(Satisfaction.NumRecords);
for (const auto &DetailRecord : Satisfaction) {
- auto *E = dyn_cast<Expr *>(DetailRecord);
- Record.push_back(/* IsDiagnostic */ E == nullptr);
- if (E)
- Record.AddStmt(E);
- else {
- auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord);
+ if (auto *Diag = dyn_cast<const ConstraintSubstitutionDiagnostic *>(
+ DetailRecord)) {
+ Record.push_back(/*Kind=*/0);
Record.AddSourceLocation(Diag->first);
Record.AddString(Diag->second);
+ continue;
+ }
+ if (auto *E = dyn_cast<const Expr *>(DetailRecord)) {
+ Record.push_back(/*Kind=*/1);
+ Record.AddStmt(const_cast<Expr *>(E));
+ } else {
+ Record.push_back(/*Kind=*/2);
+ auto *CR = cast<const ConceptReference *>(DetailRecord);
+ Record.AddConceptReference(CR);
}
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
index d0a9b20..b8095a5 100644
--- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -61,6 +61,7 @@ add_clang_library(clangStaticAnalyzerCore
clangBasic
clangCrossTU
clangFrontend
+ clangIndex
clangLex
clangRewrite
clangToolingCore
diff --git a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
index b7f9044..62ae62f2f 100644
--- a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
+++ b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
@@ -9,7 +9,9 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
#include "clang/AST/DeclBase.h"
#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FileSystem.h"
@@ -38,6 +40,7 @@ struct Registry {
};
std::vector<Snapshot> Snapshots;
+ std::string EscapedCPPFileName;
};
} // namespace
@@ -69,7 +72,7 @@ static void checkStatName(const EntryPointStat *M) {
}
}
-void EntryPointStat::lockRegistry() {
+void EntryPointStat::lockRegistry(llvm::StringRef CPPFileName) {
auto CmpByNames = [](const EntryPointStat *L, const EntryPointStat *R) {
return L->name() < R->name();
};
@@ -78,6 +81,8 @@ void EntryPointStat::lockRegistry() {
enumerateStatVectors(
[](const auto &Stats) { llvm::for_each(Stats, checkStatName); });
StatsRegistry->IsLocked = true;
+ llvm::raw_string_ostream OS(StatsRegistry->EscapedCPPFileName);
+ llvm::printEscapedString(CPPFileName, OS);
}
[[maybe_unused]] static bool isRegistered(llvm::StringLiteral Name) {
@@ -144,15 +149,27 @@ static std::vector<llvm::StringLiteral> getStatNames() {
return Ret;
}
+static std::string getUSR(const Decl *D) {
+ llvm::SmallVector<char> Buf;
+ if (index::generateUSRForDecl(D, Buf)) {
+ assert(false && "This should never fail");
+ return AnalysisDeclContext::getFunctionName(D);
+ }
+ return llvm::toStringRef(Buf).str();
+}
+
void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
OS << '"';
+ llvm::printEscapedString(getUSR(EntryPoint), OS);
+ OS << "\",\"";
+ OS << StatsRegistry->EscapedCPPFileName << "\",\"";
llvm::printEscapedString(
clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
- OS << "\", ";
+ OS << "\",";
auto PrintAsBool = [&OS](bool B) { OS << (B ? "true" : "false"); };
- llvm::interleaveComma(BoolStatValues, OS, PrintAsBool);
- OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ", ");
- llvm::interleaveComma(UnsignedStatValues, OS);
+ llvm::interleave(BoolStatValues, OS, PrintAsBool, ",");
+ OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ",");
+ llvm::interleave(UnsignedStatValues, OS, [&OS](unsigned U) { OS << U; }, ",");
}
static std::vector<bool> consumeBoolStats() {
@@ -181,8 +198,8 @@ void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {
}
void EntryPointStat::dumpStatsAsCSV(llvm::raw_ostream &OS) {
- OS << "EntryPoint, ";
- llvm::interleaveComma(getStatNames(), OS);
+ OS << "USR,File,DebugName,";
+ llvm::interleave(getStatNames(), OS, [&OS](const auto &a) { OS << a; }, ",");
OS << "\n";
std::vector<std::string> Rows;
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 53466e7..cf01e2f 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -51,6 +51,9 @@ STAT_COUNTER(NumFunctionTopLevel, "The # of functions at top level.");
ALWAYS_ENABLED_STATISTIC(NumFunctionsAnalyzed,
"The # of functions and blocks analyzed (as top level "
"with inlining turned on).");
+ALWAYS_ENABLED_STATISTIC(
+ NumFunctionsAnalyzedSyntaxOnly,
+ "The # of functions analyzed by syntax checkers only.");
ALWAYS_ENABLED_STATISTIC(NumBlocksInAnalyzedFunctions,
"The # of basic blocks in the analyzed functions.");
ALWAYS_ENABLED_STATISTIC(
@@ -65,6 +68,15 @@ STAT_MAX(MaxCFGSize, "The maximum number of basic blocks in a function.");
namespace {
+StringRef getMainFileName(const CompilerInvocation &Invocation) {
+ if (!Invocation.getFrontendOpts().Inputs.empty()) {
+ const FrontendInputFile &Input = Invocation.getFrontendOpts().Inputs[0];
+ return Input.isFile() ? Input.getFile()
+ : Input.getBuffer().getBufferIdentifier();
+ }
+ return "<no input>";
+}
+
class AnalysisConsumer : public AnalysisASTConsumer,
public DynamicRecursiveASTVisitor {
enum {
@@ -125,7 +137,8 @@ public:
PP(CI.getPreprocessor()), OutDir(outdir), Opts(opts), Plugins(plugins),
Injector(std::move(injector)), CTU(CI),
MacroExpansions(CI.getLangOpts()) {
- EntryPointStat::lockRegistry();
+
+ EntryPointStat::lockRegistry(getMainFileName(CI.getInvocation()));
DigestAnalyzerOptions();
if (Opts.AnalyzerDisplayProgress || Opts.PrintStats ||
@@ -588,10 +601,10 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
// If the user wanted to analyze a specific function and the number of basic
// blocks analyzed is zero, than the user might not specified the function
// name correctly.
- // FIXME: The user might have analyzed the requested function in Syntax mode,
- // but we are unaware of that.
- if (!Opts.AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0)
+ if (!Opts.AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0 &&
+ NumFunctionsAnalyzedSyntaxOnly == 0) {
reportAnalyzerFunctionMisuse(Opts, *Ctx);
+ }
}
void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
@@ -659,8 +672,11 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
AnalysisConsumer::AnalysisMode
AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
if (!Opts.AnalyzeSpecificFunction.empty() &&
- AnalysisDeclContext::getFunctionName(D) != Opts.AnalyzeSpecificFunction)
+ AnalysisDeclContext::getFunctionName(D) != Opts.AnalyzeSpecificFunction &&
+ cross_tu::CrossTranslationUnitContext::getLookupName(D).value_or("") !=
+ Opts.AnalyzeSpecificFunction) {
return AM_None;
+ }
// Unless -analyze-all is specified, treat decls differently depending on
// where they came from:
@@ -723,6 +739,7 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
SyntaxCheckTimer->startTimer();
}
checkerMgr->runCheckersOnASTBody(D, *Mgr, BR);
+ ++NumFunctionsAnalyzedSyntaxOnly;
if (SyntaxCheckTimer) {
SyntaxCheckTimer->stopTimer();
llvm::TimeRecord CheckerEndTime = SyntaxCheckTimer->getTotalTime();