aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTConcept.cpp2
-rw-r--r--clang/lib/AST/ASTContext.cpp27
-rw-r--r--clang/lib/AST/ASTImporter.cpp4
-rw-r--r--clang/lib/AST/ASTStructuralEquivalence.cpp3
-rw-r--r--clang/lib/AST/ByteCode/ByteCodeEmitter.cpp24
-rw-r--r--clang/lib/AST/ByteCode/ByteCodeEmitter.h2
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp29
-rw-r--r--clang/lib/AST/ByteCode/Context.cpp10
-rw-r--r--clang/lib/AST/ByteCode/EvalEmitter.cpp4
-rw-r--r--clang/lib/AST/ByteCode/Function.h4
-rw-r--r--clang/lib/AST/ByteCode/Interp.cpp48
-rw-r--r--clang/lib/AST/ByteCode/Interp.h13
-rw-r--r--clang/lib/AST/ComputeDependence.cpp2
-rw-r--r--clang/lib/AST/Decl.cpp3
-rw-r--r--clang/lib/AST/DeclCXX.cpp2
-rw-r--r--clang/lib/AST/DeclPrinter.cpp8
-rw-r--r--clang/lib/AST/DeclTemplate.cpp39
-rw-r--r--clang/lib/AST/ExprCXX.cpp36
-rw-r--r--clang/lib/AST/ExprConstant.cpp2
-rw-r--r--clang/lib/AST/ItaniumMangle.cpp4
-rw-r--r--clang/lib/AST/Mangle.cpp4
-rw-r--r--clang/lib/AST/OpenACCClause.cpp19
-rw-r--r--clang/lib/AST/QualTypeNames.cpp2
-rw-r--r--clang/lib/AST/StmtProfile.cpp3
-rw-r--r--clang/lib/AST/TemplateBase.cpp11
-rw-r--r--clang/lib/AST/Type.cpp7
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp6
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp32
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.cpp3
-rw-r--r--clang/lib/CodeGen/Targets/RISCV.cpp3
-rw-r--r--clang/lib/Driver/ToolChains/AMDGPU.cpp3
-rw-r--r--clang/lib/Driver/ToolChains/CommonArgs.cpp37
-rw-r--r--clang/lib/Frontend/FrontendAction.cpp83
-rw-r--r--clang/lib/Parse/ParseDecl.cpp2
-rw-r--r--clang/lib/Parse/ParseTemplate.cpp85
-rw-r--r--clang/lib/Parse/ParseTentative.cpp2
-rw-r--r--clang/lib/Parse/Parser.cpp7
-rw-r--r--clang/lib/Sema/Sema.cpp4
-rw-r--r--clang/lib/Sema/SemaAMDGPU.cpp3
-rw-r--r--clang/lib/Sema/SemaChecking.cpp3
-rw-r--r--clang/lib/Sema/SemaDecl.cpp22
-rw-r--r--clang/lib/Sema/SemaExpr.cpp5
-rw-r--r--clang/lib/Sema/SemaInit.cpp2
-rw-r--r--clang/lib/Sema/SemaOpenACC.cpp78
-rw-r--r--clang/lib/Sema/SemaOpenACCClause.cpp12
-rw-r--r--clang/lib/Sema/SemaRISCV.cpp12
-rw-r--r--clang/lib/Sema/SemaStmt.cpp6
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp391
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp210
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp32
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp6
-rw-r--r--clang/lib/Sema/SemaTemplateVariadic.cpp10
-rw-r--r--clang/lib/Sema/SemaType.cpp10
-rw-r--r--clang/lib/Sema/TreeTransform.h85
-rw-r--r--clang/lib/Serialization/ASTReader.cpp10
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp1
-rw-r--r--clang/lib/Serialization/ASTReaderStmt.cpp8
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp5
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp188
61 files changed, 1246 insertions, 434 deletions
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index 91ab66f..2243ac0 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -86,7 +86,7 @@ ConceptReference *
ConceptReference::Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
DeclarationNameInfo ConceptNameInfo,
- NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
return new (C) ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo,
FoundDecl, NamedConcept, ArgsAsWritten);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 16cf114..3a16111 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -80,6 +80,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/Support/Capacity.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MD5.h"
@@ -717,13 +718,13 @@ comments::FullComment *ASTContext::getCommentForDecl(
return FC;
}
-void
-ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
- const ASTContext &C,
- TemplateTemplateParmDecl *Parm) {
+void ASTContext::CanonicalTemplateTemplateParm::Profile(
+ llvm::FoldingSetNodeID &ID, const ASTContext &C,
+ TemplateTemplateParmDecl *Parm) {
ID.AddInteger(Parm->getDepth());
ID.AddInteger(Parm->getPosition());
ID.AddBoolean(Parm->isParameterPack());
+ ID.AddInteger(Parm->templateParameterKind());
TemplateParameterList *Params = Parm->getTemplateParameters();
ID.AddInteger(Params->size());
@@ -829,7 +830,9 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
TemplateTemplateParmDecl *CanonTTP = TemplateTemplateParmDecl::Create(
*this, getTranslationUnitDecl(), SourceLocation(), TTP->getDepth(),
- TTP->getPosition(), TTP->isParameterPack(), nullptr, /*Typename=*/false,
+ TTP->getPosition(), TTP->isParameterPack(), nullptr,
+ TTP->templateParameterKind(),
+ /*Typename=*/false,
TemplateParameterList::Create(*this, SourceLocation(), SourceLocation(),
CanonParams, SourceLocation(),
/*RequiresClause=*/nullptr));
@@ -6643,7 +6646,7 @@ ASTContext::getUnaryTransformType(QualType BaseType, QualType UnderlyingType,
QualType ASTContext::getAutoTypeInternal(
QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
- bool IsPack, ConceptDecl *TypeConstraintConcept,
+ bool IsPack, TemplateDecl *TypeConstraintConcept,
ArrayRef<TemplateArgument> TypeConstraintArgs, bool IsCanon) const {
if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto &&
!TypeConstraintConcept && !IsDependent)
@@ -6652,7 +6655,8 @@ QualType ASTContext::getAutoTypeInternal(
// Look in the folding set for an existing type.
llvm::FoldingSetNodeID ID;
bool IsDeducedDependent =
- !DeducedType.isNull() && DeducedType->isDependentType();
+ isa_and_nonnull<TemplateTemplateParmDecl>(TypeConstraintConcept) ||
+ (!DeducedType.isNull() && DeducedType->isDependentType());
AutoType::Profile(ID, *this, DeducedType, Keyword,
IsDependent || IsDeducedDependent, TypeConstraintConcept,
TypeConstraintArgs);
@@ -6665,7 +6669,8 @@ QualType ASTContext::getAutoTypeInternal(
Canon = DeducedType.getCanonicalType();
} else if (TypeConstraintConcept) {
bool AnyNonCanonArgs = false;
- ConceptDecl *CanonicalConcept = TypeConstraintConcept->getCanonicalDecl();
+ auto *CanonicalConcept =
+ cast<TemplateDecl>(TypeConstraintConcept->getCanonicalDecl());
auto CanonicalConceptArgs = ::getCanonicalTemplateArguments(
*this, TypeConstraintArgs, AnyNonCanonArgs);
if (CanonicalConcept != TypeConstraintConcept || AnyNonCanonArgs) {
@@ -6701,7 +6706,7 @@ QualType ASTContext::getAutoTypeInternal(
QualType
ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
bool IsDependent, bool IsPack,
- ConceptDecl *TypeConstraintConcept,
+ TemplateDecl *TypeConstraintConcept,
ArrayRef<TemplateArgument> TypeConstraintArgs) const {
assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack");
assert((!IsDependent || DeducedType.isNull()) &&
@@ -14387,8 +14392,8 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
if (KW != AY->getKeyword())
return QualType();
- ConceptDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(),
- AY->getTypeConstraintConcept());
+ TemplateDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(),
+ AY->getTypeConstraintConcept());
SmallVector<TemplateArgument, 8> As;
if (CD &&
getCommonTemplateArguments(Ctx, As, AX->getTypeConstraintArguments(),
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index b9bdabe0b..8e2927b 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -6268,8 +6268,8 @@ ASTNodeImporter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
ToD, D, Importer.getToContext(),
Importer.getToContext().getTranslationUnitDecl(), *LocationOrErr,
D->getDepth(), D->getPosition(), D->isParameterPack(),
- (*NameOrErr).getAsIdentifierInfo(), D->wasDeclaredWithTypename(),
- *TemplateParamsOrErr))
+ (*NameOrErr).getAsIdentifierInfo(), D->templateParameterKind(),
+ D->wasDeclaredWithTypename(), *TemplateParamsOrErr))
return ToD;
if (Error Err = importTemplateParameterDefaultArgument(D, ToD))
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 22bb4cb..0e2023f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2313,7 +2313,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
}
// Check template parameter lists.
- return IsStructurallyEquivalent(Context, D1->getTemplateParameters(),
+ return D1->templateParameterKind() == D2->templateParameterKind() &&
+ IsStructurallyEquivalent(Context, D1->getTemplateParameters(),
D2->getTemplateParameters());
}
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index d474605..8e7206e 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -135,8 +135,8 @@ int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
/// Helper to write bytecode and bail out if 32-bit offsets become invalid.
/// Pointers will be automatically marshalled as 32-bit IDs.
template <typename T>
-static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
- bool &Success) {
+static void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code,
+ const T &Val, bool &Success) {
size_t ValPos = Code.size();
size_t Size;
@@ -153,7 +153,7 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
// Access must be aligned!
assert(aligned(ValPos));
assert(aligned(ValPos + Size));
- Code.resize(ValPos + Size);
+ Code.resize_for_overwrite(ValPos + Size);
if constexpr (!std::is_pointer_v<T>) {
new (Code.data() + ValPos) T(Val);
@@ -166,7 +166,7 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
/// Emits a serializable value. These usually (potentially) contain
/// heap-allocated memory and aren't trivially copyable.
template <typename T>
-static void emitSerialized(std::vector<std::byte> &Code, const T &Val,
+static void emitSerialized(llvm::SmallVectorImpl<std::byte> &Code, const T &Val,
bool &Success) {
size_t ValPos = Code.size();
size_t Size = align(Val.bytesToSerialize());
@@ -179,32 +179,32 @@ static void emitSerialized(std::vector<std::byte> &Code, const T &Val,
// Access must be aligned!
assert(aligned(ValPos));
assert(aligned(ValPos + Size));
- Code.resize(ValPos + Size);
+ Code.resize_for_overwrite(ValPos + Size);
Val.serialize(Code.data() + ValPos);
}
template <>
-void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
- bool &Success) {
+void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code,
+ const Floating &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}
template <>
-void emit(Program &P, std::vector<std::byte> &Code,
+void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code,
const IntegralAP<false> &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}
template <>
-void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,
- bool &Success) {
+void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code,
+ const IntegralAP<true> &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}
template <>
-void emit(Program &P, std::vector<std::byte> &Code, const FixedPoint &Val,
- bool &Success) {
+void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code,
+ const FixedPoint &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 9e9dd5e..8a02911 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -88,7 +88,7 @@ private:
/// Location of label relocations.
llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs;
/// Program code.
- std::vector<std::byte> Code;
+ llvm::SmallVector<std::byte> Code;
/// Opcode to expression mapping.
SourceMap SrcMap;
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 6e451ac..5bcac39 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -201,6 +201,28 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, CE);
OptPrimType SubExprT = classify(SubExpr->getType());
+ // Try to load the value directly. This is purely a performance
+ // optimization.
+ if (SubExprT) {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {
+ const ValueDecl *D = DRE->getDecl();
+ bool IsReference = D->getType()->isReferenceType();
+
+ if (!IsReference) {
+ if (Context::shouldBeGloballyIndexed(D)) {
+ if (auto GlobalIndex = P.getGlobal(D))
+ return this->emitGetGlobal(*SubExprT, *GlobalIndex, CE);
+ } else if (auto It = Locals.find(D); It != Locals.end()) {
+ return this->emitGetLocal(*SubExprT, It->second.Offset, CE);
+ } else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
+ if (auto It = this->Params.find(PVD); It != this->Params.end()) {
+ return this->emitGetParam(*SubExprT, It->second.Offset, CE);
+ }
+ }
+ }
+ }
+ }
+
// Prepare storage for the result.
if (!Initializing && !SubExprT) {
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
@@ -3857,10 +3879,7 @@ template <class Emitter>
bool Compiler<Emitter>::VisitAddrLabelExpr(const AddrLabelExpr *E) {
assert(E->getType()->isVoidPointerType());
- unsigned Offset =
- allocateLocalPrimitive(E->getLabel(), PT_Ptr, /*IsConst=*/true);
-
- return this->emitGetLocal(PT_Ptr, Offset, E);
+ return this->emitDummyPtr(E, E);
}
template <class Emitter>
@@ -5895,7 +5914,7 @@ bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
const CXXRecordDecl *ClosureClass = MD->getParent();
const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
- assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
+ assert(ClosureClass->captures().empty());
const Function *Func = this->getFunction(LambdaCallOp);
if (!Func)
return false;
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 7215e1dd..f7f528c 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -45,12 +45,12 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
Compiler<ByteCodeEmitter>(*this, *P).compileFunc(
FD, const_cast<Function *>(Func));
- ++EvalID;
- // And run it.
- if (!Run(Parent, Func))
+ if (!Func->isValid())
return false;
- return Func->isValid();
+ ++EvalID;
+ // And run it.
+ return Run(Parent, Func);
}
void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
@@ -474,7 +474,7 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
IsLambdaStaticInvoker = true;
const CXXRecordDecl *ClosureClass = MD->getParent();
- assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
+ assert(ClosureClass->captures().empty());
if (ClosureClass->isGenericLambda()) {
const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
assert(MD->isFunctionTemplateSpecialization() &&
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 81ebc56..fd7f342 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -282,6 +282,10 @@ bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
using T = typename PrimConv<OpType>::T;
Block *B = getLocal(I);
+
+ if (!CheckLocalLoad(S, OpPC, Pointer(B)))
+ return false;
+
S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
return true;
}
diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h
index 64bffc4..92363b6 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -236,7 +236,7 @@ private:
bool HasRVO, bool IsLambdaStaticInvoker);
/// Sets the code of a function.
- void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode,
+ void setCode(unsigned NewFrameSize, llvm::SmallVector<std::byte> &&NewCode,
SourceMap &&NewSrcMap, llvm::SmallVector<Scope, 2> &&NewScopes,
bool NewHasBody) {
FrameSize = NewFrameSize;
@@ -266,7 +266,7 @@ private:
/// Size of the argument stack.
unsigned ArgSize;
/// Program code.
- std::vector<std::byte> Code;
+ llvm::SmallVector<std::byte> Code;
/// Opcode-to-expression mapping.
SourceMap SrcMap;
/// List of block descriptors.
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 224d65c..f2366f6 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -715,23 +715,6 @@ static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}
-bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (Ptr.isInitialized())
- return true;
-
- assert(S.getLangOpts().CPlusPlus);
- const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl());
- if ((!VD->hasConstantInitialization() &&
- VD->mightBeUsableInConstantExpressions(S.getASTContext())) ||
- (S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 &&
- !VD->hasICEInitializer(S.getASTContext()))) {
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
- S.Note(VD->getLocation(), diag::note_declared_at);
- }
- return false;
-}
-
static bool CheckWeak(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!Ptr.isWeak())
return true;
@@ -745,6 +728,37 @@ static bool CheckWeak(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
}
+// The list of checks here is just the one from CheckLoad, but with the
+// ones removed that are impossible on primitive global values.
+// For example, since those can't be members of structs, they also can't
+// be mutable.
+bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckConstant(S, OpPC, Ptr))
+ return false;
+ if (!CheckDummy(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckWeak(S, OpPC, Ptr))
+ return false;
+ if (!CheckVolatile(S, OpPC, Ptr, AK_Read))
+ return false;
+ return true;
+}
+
+// Similarly, for local loads.
+bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckVolatile(S, OpPC, Ptr, AK_Read))
+ return false;
+ return true;
+}
+
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
if (!CheckLive(S, OpPC, Ptr, AK))
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9a325ab..61e7769 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -91,8 +91,9 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
-/// Check if a global variable is initialized.
-bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+/// Checks a direct load of a primitive value from a global or local variable.
+bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a value can be stored in a block.
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
@@ -1465,14 +1466,8 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Ptr = S.P.getPtrGlobal(I);
- if (!CheckConstant(S, OpPC, Ptr.getFieldDesc()))
- return false;
- if (Ptr.isExtern())
- return false;
- // If a global variable is uninitialized, that means the initializer we've
- // compiled for it wasn't a constant expression. Diagnose that.
- if (!CheckGlobalInitialized(S, OpPC, Ptr))
+ if (!CheckGlobalLoad(S, OpPC, Ptr))
return false;
S.Stk.push<T>(Ptr.deref<T>());
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 14ec93e..87334d9 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -806,7 +806,7 @@ clang::computeDependence(OverloadExpr *E, bool KnownDependent,
~NestedNameSpecifierDependence::Dependent);
for (auto *D : E->decls()) {
if (D->getDeclContext()->isDependentContext() ||
- isa<UnresolvedUsingValueDecl>(D))
+ isa<UnresolvedUsingValueDecl>(D) || isa<TemplateTemplateParmDecl>(D))
Deps |= ExprDependence::TypeValueInstantiation;
}
// If we have explicit template arguments, check for dependent
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 1588be4..5471f31 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1233,6 +1233,9 @@ getExplicitVisibilityAux(const NamedDecl *ND,
bool IsMostRecent) {
assert(!IsMostRecent || ND == ND->getMostRecentDecl());
+ if (isa<ConceptDecl>(ND))
+ return {};
+
// Check the declaration itself first.
if (std::optional<Visibility> V = getVisibilityOf(ND, kind))
return V;
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 673e3f7..037a28c4 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1983,7 +1983,7 @@ CXXRecordDecl::getVisibleConversionFunctions() const {
ASTContext &Ctx = getASTContext();
ASTUnresolvedSet *Set;
- if (bases_begin() == bases_end()) {
+ if (bases().empty()) {
// If root class, all conversions are visible.
Set = &data().Conversions.get(Ctx);
} else {
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 9273f58..f4265dd0 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -1443,7 +1443,7 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
lastPos = pos + 1;
}
- if (OMD->param_begin() == OMD->param_end())
+ if (OMD->parameters().empty())
Out << name;
if (OMD->isVariadic())
@@ -1480,8 +1480,7 @@ void DeclPrinter::VisitObjCImplementationDecl(ObjCImplementationDecl *OID) {
}
Indentation -= Policy.Indentation;
Out << "}\n";
- }
- else if (SID || (OID->decls_begin() != OID->decls_end())) {
+ } else if (SID || !OID->decls().empty()) {
Out << "\n";
eolnOut = true;
}
@@ -1540,8 +1539,7 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) {
}
Indentation -= Policy.Indentation;
Out << "}\n";
- }
- else if (SID || (OID->decls_begin() != OID->decls_end())) {
+ } else if (SID || !OID->decls().empty()) {
Out << "\n";
eolnOut = true;
}
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 5035f2d..bc4a299 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -162,6 +162,7 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
}
const auto *TTP = cast<TemplateTemplateParmDecl>(D);
ID.AddInteger(2);
+ ID.AddInteger(TTP->templateParameterKind());
ID.AddBoolean(TTP->isParameterPack());
TTP->getTemplateParameters()->Profile(ID, C);
}
@@ -184,7 +185,8 @@ unsigned TemplateParameterList::getMinRequiredArguments() const {
} else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
if (NTTP->hasDefaultArgument())
break;
- } else if (cast<TemplateTemplateParmDecl>(P)->hasDefaultArgument())
+ } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P);
+ TTP && TTP->hasDefaultArgument())
break;
++NumRequiredArgs;
@@ -873,38 +875,40 @@ void TemplateTemplateParmDecl::anchor() {}
TemplateTemplateParmDecl::TemplateTemplateParmDecl(
DeclContext *DC, SourceLocation L, unsigned D, unsigned P,
- IdentifierInfo *Id, bool Typename, TemplateParameterList *Params,
- ArrayRef<TemplateParameterList *> Expansions)
+ IdentifierInfo *Id, TemplateNameKind Kind, bool Typename,
+ TemplateParameterList *Params, ArrayRef<TemplateParameterList *> Expansions)
: TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
- TemplateParmPosition(D, P), Typename(Typename), ParameterPack(true),
- ExpandedParameterPack(true), NumExpandedParams(Expansions.size()) {
+ TemplateParmPosition(D, P), ParameterKind(Kind), Typename(Typename),
+ ParameterPack(true), ExpandedParameterPack(true),
+ NumExpandedParams(Expansions.size()) {
llvm::uninitialized_copy(Expansions, getTrailingObjects());
}
-TemplateTemplateParmDecl *
-TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC,
- SourceLocation L, unsigned D, unsigned P,
- bool ParameterPack, IdentifierInfo *Id,
- bool Typename, TemplateParameterList *Params) {
+TemplateTemplateParmDecl *TemplateTemplateParmDecl::Create(
+ const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+ unsigned P, bool ParameterPack, IdentifierInfo *Id, TemplateNameKind Kind,
+ bool Typename, TemplateParameterList *Params) {
return new (C, DC) TemplateTemplateParmDecl(DC, L, D, P, ParameterPack, Id,
- Typename, Params);
+ Kind, Typename, Params);
}
TemplateTemplateParmDecl *
TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC,
SourceLocation L, unsigned D, unsigned P,
- IdentifierInfo *Id, bool Typename,
- TemplateParameterList *Params,
+ IdentifierInfo *Id, TemplateNameKind Kind,
+ bool Typename, TemplateParameterList *Params,
ArrayRef<TemplateParameterList *> Expansions) {
return new (C, DC,
additionalSizeToAlloc<TemplateParameterList *>(Expansions.size()))
- TemplateTemplateParmDecl(DC, L, D, P, Id, Typename, Params, Expansions);
+ TemplateTemplateParmDecl(DC, L, D, P, Id, Kind, Typename, Params,
+ Expansions);
}
TemplateTemplateParmDecl *
TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
- return new (C, ID) TemplateTemplateParmDecl(nullptr, SourceLocation(), 0, 0,
- false, nullptr, false, nullptr);
+ return new (C, ID) TemplateTemplateParmDecl(
+ nullptr, SourceLocation(), 0, 0, false, nullptr,
+ TemplateNameKind::TNK_Type_template, false, nullptr);
}
TemplateTemplateParmDecl *
@@ -913,7 +917,8 @@ TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
auto *TTP =
new (C, ID, additionalSizeToAlloc<TemplateParameterList *>(NumExpansions))
TemplateTemplateParmDecl(nullptr, SourceLocation(), 0, 0, nullptr,
- false, nullptr, {});
+ TemplateNameKind::TNK_Type_template, false,
+ nullptr, {});
TTP->NumExpandedParams = NumExpansions;
return TTP;
}
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 5833a64..a099e97 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -396,6 +396,16 @@ SourceLocation CXXPseudoDestructorExpr::getEndLoc() const {
return End;
}
+static bool UnresolvedLookupExprIsVariableOrConceptParameterPack(
+ UnresolvedSetIterator Begin, UnresolvedSetIterator End) {
+ if (std::distance(Begin, End) != 1)
+ return false;
+ NamedDecl *ND = *Begin;
+ if (const auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND))
+ return TTP->isParameterPack();
+ return false;
+}
+
// UnresolvedLookupExpr
UnresolvedLookupExpr::UnresolvedLookupExpr(
const ASTContext &Context, CXXRecordDecl *NamingClass,
@@ -404,9 +414,11 @@ UnresolvedLookupExpr::UnresolvedLookupExpr(
const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin,
UnresolvedSetIterator End, bool KnownDependent,
bool KnownInstantiationDependent)
- : OverloadExpr(UnresolvedLookupExprClass, Context, QualifierLoc,
- TemplateKWLoc, NameInfo, TemplateArgs, Begin, End,
- KnownDependent, KnownInstantiationDependent, false),
+ : OverloadExpr(
+ UnresolvedLookupExprClass, Context, QualifierLoc, TemplateKWLoc,
+ NameInfo, TemplateArgs, Begin, End, KnownDependent,
+ KnownInstantiationDependent,
+ UnresolvedLookupExprIsVariableOrConceptParameterPack(Begin, End)),
NamingClass(NamingClass) {
UnresolvedLookupExprBits.RequiresADL = RequiresADL;
}
@@ -1714,8 +1726,8 @@ SizeOfPackExpr *SizeOfPackExpr::CreateDeserialized(ASTContext &Context,
return new (Storage) SizeOfPackExpr(EmptyShell(), NumPartialArgs);
}
-NonTypeTemplateParmDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
- return cast<NonTypeTemplateParmDecl>(
+NamedDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
+ return cast<NamedDecl>(
getReplacedTemplateParameterList(getAssociatedDecl())->asArray()[Index]);
}
@@ -1758,9 +1770,12 @@ QualType SubstNonTypeTemplateParmExpr::getParameterType(
const ASTContext &Context) const {
// Note that, for a class type NTTP, we will have an lvalue of type 'const
// T', so we can't just compute this from the type and value category.
+
+ QualType Type = getType();
+
if (isReferenceParameter())
- return Context.getLValueReferenceType(getType());
- return getType().getUnqualifiedType();
+ return Context.getLValueReferenceType(Type);
+ return Type.getUnqualifiedType();
}
SubstNonTypeTemplateParmPackExpr::SubstNonTypeTemplateParmPackExpr(
@@ -1997,9 +2012,10 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
NumExpansions(NumExpansions) {
CXXFoldExprBits.Opcode = Opcode;
// We rely on asserted invariant to distinguish left and right folds.
- assert(((LHS && LHS->containsUnexpandedParameterPack()) !=
- (RHS && RHS->containsUnexpandedParameterPack())) &&
- "Exactly one of LHS or RHS should contain an unexpanded pack");
+ if (LHS && RHS)
+ assert(LHS->containsUnexpandedParameterPack() !=
+ RHS->containsUnexpandedParameterPack() &&
+ "Exactly one of LHS or RHS should contain an unexpanded pack");
SubExprs[SubExpr::Callee] = Callee;
SubExprs[SubExpr::LHS] = LHS;
SubExprs[SubExpr::RHS] = RHS;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 20b7c44..3a47fa8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8436,7 +8436,7 @@ public:
// doesn't have an implicit argument passed in.
const CXXRecordDecl *ClosureClass = MD->getParent();
assert(
- ClosureClass->captures_begin() == ClosureClass->captures_end() &&
+ ClosureClass->captures().empty() &&
"Number of captures must be zero for conversion to function-ptr");
const CXXMethodDecl *LambdaCallOp =
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2a66793..5233648 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -529,7 +529,7 @@ private:
void mangleUnqualifiedBlock(const BlockDecl *Block);
void mangleTemplateParamDecl(const NamedDecl *Decl);
void mangleTemplateParameterList(const TemplateParameterList *Params);
- void mangleTypeConstraint(const ConceptDecl *Concept,
+ void mangleTypeConstraint(const TemplateDecl *Concept,
ArrayRef<TemplateArgument> Arguments);
void mangleTypeConstraint(const TypeConstraint *Constraint);
void mangleRequiresClause(const Expr *RequiresClause);
@@ -2080,7 +2080,7 @@ void CXXNameMangler::mangleTemplateParameterList(
}
void CXXNameMangler::mangleTypeConstraint(
- const ConceptDecl *Concept, ArrayRef<TemplateArgument> Arguments) {
+ const TemplateDecl *Concept, ArrayRef<TemplateArgument> Arguments) {
const DeclContext *DC = Context.getEffectiveDeclContext(Concept);
if (!Arguments.empty())
mangleTemplateName(Concept, Arguments);
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 9652fdb..0bfb51c 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -161,9 +161,9 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
- // If the label isn't literal, or if this is an alias for an LLVM intrinsic,
+ // If the label is an alias for an LLVM intrinsic,
// do not add a "\01" prefix.
- if (!ALA->getIsLiteralLabel() || ALA->getLabel().starts_with("llvm.")) {
+ if (ALA->getLabel().starts_with("llvm.")) {
Out << ALA->getLabel();
return;
}
diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp
index 60ec10a..f21e645 100644
--- a/clang/lib/AST/OpenACCClause.cpp
+++ b/clang/lib/AST/OpenACCClause.cpp
@@ -314,14 +314,17 @@ OpenACCTileClause *OpenACCTileClause::Create(const ASTContext &C,
return new (Mem) OpenACCTileClause(BeginLoc, LParenLoc, SizeExprs, EndLoc);
}
-OpenACCPrivateClause *OpenACCPrivateClause::Create(const ASTContext &C,
- SourceLocation BeginLoc,
- SourceLocation LParenLoc,
- ArrayRef<Expr *> VarList,
- SourceLocation EndLoc) {
- void *Mem = C.Allocate(
- OpenACCPrivateClause::totalSizeToAlloc<Expr *>(VarList.size()));
- return new (Mem) OpenACCPrivateClause(BeginLoc, LParenLoc, VarList, EndLoc);
+OpenACCPrivateClause *
+OpenACCPrivateClause::Create(const ASTContext &C, SourceLocation BeginLoc,
+ SourceLocation LParenLoc, ArrayRef<Expr *> VarList,
+ ArrayRef<VarDecl *> InitRecipes,
+ SourceLocation EndLoc) {
+ assert(VarList.size() == InitRecipes.size());
+ void *Mem =
+ C.Allocate(OpenACCPrivateClause::totalSizeToAlloc<Expr *, VarDecl *>(
+ VarList.size(), InitRecipes.size()));
+ return new (Mem)
+ OpenACCPrivateClause(BeginLoc, LParenLoc, VarList, InitRecipes, EndLoc);
}
OpenACCFirstPrivateClause *OpenACCFirstPrivateClause::Create(
diff --git a/clang/lib/AST/QualTypeNames.cpp b/clang/lib/AST/QualTypeNames.cpp
index b43bcd8..9731b3a 100644
--- a/clang/lib/AST/QualTypeNames.cpp
+++ b/clang/lib/AST/QualTypeNames.cpp
@@ -273,7 +273,7 @@ static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
//
// Make the situation is 'useable' but looking a bit odd by
// picking a random instance as the declaring context.
- if (ClassTempl->spec_begin() != ClassTempl->spec_end()) {
+ if (!ClassTempl->specializations().empty()) {
Decl = *(ClassTempl->spec_begin());
Outer = dyn_cast<NamedDecl>(Decl);
OuterNS = dyn_cast<NamespaceDecl>(Decl);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index c61450e..57834ca 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2636,6 +2636,9 @@ void OpenACCClauseProfiler::VisitCollapseClause(
void OpenACCClauseProfiler::VisitPrivateClause(
const OpenACCPrivateClause &Clause) {
VisitClauseWithVarList(Clause);
+
+ for (auto *VD : Clause.getInitRecipes())
+ Profiler.VisitDecl(VD);
}
void OpenACCClauseProfiler::VisitFirstPrivateClause(
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 7c89dea..7a0f740 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -339,6 +339,17 @@ bool TemplateArgument::isPackExpansion() const {
llvm_unreachable("Invalid TemplateArgument Kind!");
}
+bool TemplateArgument::isConceptOrConceptTemplateParameter() const {
+ if (getKind() == TemplateArgument::Template) {
+ if (isa<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
+ return true;
+ else if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
+ getAsTemplate().getAsTemplateDecl()))
+ return TTP->templateParameterKind() == TNK_Concept_template;
+ }
+ return false;
+}
+
bool TemplateArgument::containsUnexpandedParameterPack() const {
return getDependence() & TemplateArgumentDependence::UnexpandedPack;
}
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 7444a2f..141edc8 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5338,7 +5338,7 @@ void clang::FixedPointValueToString(SmallVectorImpl<char> &Str,
AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
TypeDependence ExtraDependence, QualType Canon,
- ConceptDecl *TypeConstraintConcept,
+ TemplateDecl *TypeConstraintConcept,
ArrayRef<TemplateArgument> TypeConstraintArgs)
: DeducedType(Auto, DeducedAsType, ExtraDependence, Canon) {
AutoTypeBits.Keyword = llvm::to_underlying(Keyword);
@@ -5346,6 +5346,9 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
this->TypeConstraintConcept = TypeConstraintConcept;
assert(TypeConstraintConcept || AutoTypeBits.NumArgs == 0);
if (TypeConstraintConcept) {
+ if (isa<TemplateTemplateParmDecl>(TypeConstraintConcept))
+ addDependence(TypeDependence::DependentInstantiation);
+
auto *ArgBuffer =
const_cast<TemplateArgument *>(getTypeConstraintArguments().data());
for (const TemplateArgument &Arg : TypeConstraintArgs) {
@@ -5361,7 +5364,7 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
QualType Deduced, AutoTypeKeyword Keyword,
- bool IsDependent, ConceptDecl *CD,
+ bool IsDependent, TemplateDecl *CD,
ArrayRef<TemplateArgument> Arguments) {
ID.AddPointer(Deduced.getAsOpaquePtr());
ID.AddInteger((unsigned)Keyword);
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp
index 32095cb..907cb5f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACC.cpp
@@ -119,7 +119,8 @@ CIRGenFunction::getOpenACCDataOperandInfo(const Expr *e) {
if (const auto *memExpr = dyn_cast<MemberExpr>(curVarExpr))
return {exprLoc, emitMemberExpr(memExpr).getPointer(), exprString,
- curVarExpr->getType(), std::move(bounds)};
+ curVarExpr->getType().getNonReferenceType().getUnqualifiedType(),
+ std::move(bounds)};
// Sema has made sure that only 4 types of things can get here, array
// subscript, array section, member expr, or DRE to a var decl (or the
@@ -127,5 +128,6 @@ CIRGenFunction::getOpenACCDataOperandInfo(const Expr *e) {
// right.
const auto *dre = cast<DeclRefExpr>(curVarExpr);
return {exprLoc, emitDeclRefLValue(dre).getPointer(), exprString,
- curVarExpr->getType(), std::move(bounds)};
+ curVarExpr->getType().getNonReferenceType().getUnqualifiedType(),
+ std::move(bounds)};
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
index 5a6e665..bb9054a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
@@ -358,8 +358,8 @@ class OpenACCClauseCIREmitter final
template <typename RecipeTy>
RecipeTy getOrCreateRecipe(ASTContext &astCtx, const Expr *varRef,
- DeclContext *dc, QualType baseType,
- mlir::Value mainOp) {
+ const VarDecl *varRecipe, DeclContext *dc,
+ QualType baseType, mlir::Value mainOp) {
mlir::ModuleOp mod =
builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
@@ -398,12 +398,6 @@ class OpenACCClauseCIREmitter final
auto recipe =
RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType());
- // Magic-up a var-decl so we can use normal init/destruction operations for
- // a variable declaration.
- VarDecl &tempDecl = *VarDecl::Create(
- astCtx, dc, varRef->getBeginLoc(), varRef->getBeginLoc(),
- &astCtx.Idents.get("openacc.private.init"), baseType,
- astCtx.getTrivialTypeSourceInfo(baseType), SC_Auto);
CIRGenFunction::AutoVarEmission tempDeclEmission{
CIRGenFunction::AutoVarEmission::invalid()};
@@ -422,9 +416,11 @@ class OpenACCClauseCIREmitter final
"OpenACC non-private recipe init");
}
- tempDeclEmission =
- cgf.emitAutoVarAlloca(tempDecl, builder.saveInsertionPoint());
- cgf.emitAutoVarInit(tempDeclEmission);
+ if (varRecipe) {
+ tempDeclEmission =
+ cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
+ cgf.emitAutoVarInit(tempDeclEmission);
+ }
mlir::acc::YieldOp::create(builder, locEnd);
}
@@ -439,7 +435,7 @@ class OpenACCClauseCIREmitter final
}
// Destroy section (doesn't currently exist).
- if (tempDecl.needsDestruction(cgf.getContext())) {
+ if (varRecipe && varRecipe->needsDestruction(cgf.getContext())) {
llvm::SmallVector<mlir::Type> argsTys{mainOp.getType()};
llvm::SmallVector<mlir::Location> argsLocs{loc};
mlir::Block *block = builder.createBlock(&recipe.getDestroyRegion(),
@@ -450,7 +446,7 @@ class OpenACCClauseCIREmitter final
mlir::Type elementTy =
mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
Address addr{block->getArgument(0), elementTy,
- cgf.getContext().getDeclAlign(&tempDecl)};
+ cgf.getContext().getDeclAlign(varRecipe)};
cgf.emitDestroy(addr, baseType,
cgf.getDestroyer(QualType::DK_cxx_destructor));
@@ -1080,9 +1076,10 @@ public:
void VisitPrivateClause(const OpenACCPrivateClause &clause) {
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
mlir::acc::LoopOp>) {
- for (const Expr *var : clause.getVarList()) {
+ for (const auto [varExpr, varRecipe] :
+ llvm::zip_equal(clause.getVarList(), clause.getInitRecipes())) {
CIRGenFunction::OpenACCDataOperandInfo opInfo =
- cgf.getOpenACCDataOperandInfo(var);
+ cgf.getOpenACCDataOperandInfo(varExpr);
auto privateOp = mlir::acc::PrivateOp::create(
builder, opInfo.beginLoc, opInfo.varValue, /*structured=*/true,
/*implicit=*/false, opInfo.name, opInfo.bounds);
@@ -1091,8 +1088,9 @@ public:
{
mlir::OpBuilder::InsertionGuard guardCase(builder);
auto recipe = getOrCreateRecipe<mlir::acc::PrivateRecipeOp>(
- cgf.getContext(), var, Decl::castToDeclContext(cgf.curFuncDecl),
- opInfo.baseType, privateOp.getResult());
+ cgf.getContext(), varExpr, varRecipe,
+ Decl::castToDeclContext(cgf.curFuncDecl), opInfo.baseType,
+ privateOp.getResult());
// TODO: OpenACC: The dialect is going to change in the near future to
// have these be on a different operation, so when that changes, we
// probably need to change these here.
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 1ce834d..5f50b1cc 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2630,7 +2630,8 @@ StringRef CGDebugInfo::getVTableName(const CXXRecordDecl *RD) {
// existing information in the DWARF. The type is assumed to be 'void *'.
void CGDebugInfo::emitVTableSymbol(llvm::GlobalVariable *VTable,
const CXXRecordDecl *RD) {
- if (!CGM.getTarget().getCXXABI().isItaniumFamily())
+ if (!CGM.getTarget().getCXXABI().isItaniumFamily() ||
+ CGM.getTarget().getTriple().isOSBinFormatCOFF())
return;
if (DebugKind <= llvm::codegenoptions::DebugLineTablesOnly)
return;
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index e3232b6..a7f9298 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -817,6 +817,9 @@ public:
case RISCVInterruptAttr::supervisor:
Kind = "supervisor";
break;
+ case RISCVInterruptAttr::rnmi:
+ Kind = "rnmi";
+ break;
case RISCVInterruptAttr::qcinest:
Kind = "qci-nest";
break;
diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp
index 0781683..5e463b9 100644
--- a/clang/lib/Driver/ToolChains/AMDGPU.cpp
+++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp
@@ -857,6 +857,9 @@ void AMDGPUToolChain::addClangTargetOptions(
CC1Args.push_back("-fvisibility=hidden");
CC1Args.push_back("-fapply-global-visibility-to-externs");
}
+
+ if (DeviceOffloadingKind == Action::OFK_None)
+ addOpenCLBuiltinsLib(getDriver(), DriverArgs, CC1Args);
}
void AMDGPUToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 334fcbb..53fd525 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -2985,6 +2985,43 @@ void tools::addHIPRuntimeLibArgs(const ToolChain &TC, Compilation &C,
}
}
+void tools::addOpenCLBuiltinsLib(const Driver &D,
+ const llvm::opt::ArgList &DriverArgs,
+ llvm::opt::ArgStringList &CC1Args) {
+ // Check whether user specifies a libclc bytecode library
+ const Arg *A = DriverArgs.getLastArg(options::OPT_libclc_lib_EQ);
+ if (!A)
+ return;
+
+ // Find device libraries in <LLVM_DIR>/lib/clang/<ver>/lib/libclc/
+ SmallString<128> LibclcPath(D.ResourceDir);
+ llvm::sys::path::append(LibclcPath, "lib", "libclc");
+
+ // If the namespec is of the form :filename, search for that file.
+ StringRef LibclcNamespec(A->getValue());
+ bool FilenameSearch = LibclcNamespec.consume_front(":");
+ SmallString<128> LibclcTargetFile(LibclcNamespec);
+
+ if (FilenameSearch && llvm::sys::fs::exists(LibclcTargetFile)) {
+ CC1Args.push_back("-mlink-builtin-bitcode");
+ CC1Args.push_back(DriverArgs.MakeArgString(LibclcTargetFile));
+ } else {
+ // Search the library paths for the file
+ if (!FilenameSearch)
+ LibclcTargetFile += ".bc";
+
+ llvm::sys::path::append(LibclcPath, LibclcTargetFile);
+ if (llvm::sys::fs::exists(LibclcPath)) {
+ CC1Args.push_back("-mlink-builtin-bitcode");
+ CC1Args.push_back(DriverArgs.MakeArgString(LibclcPath));
+ } else {
+ // Since the user requested a library, if we haven't one then report an
+ // error.
+ D.Diag(diag::err_drv_libclc_not_found) << LibclcTargetFile;
+ }
+ }
+}
+
void tools::addOutlineAtomicsArgs(const Driver &D, const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 2d69f8c..a7d6a068 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -84,29 +84,28 @@ public:
if (!IsCollectingDecls)
return;
if (!D || isa<TranslationUnitDecl>(D) || isa<LinkageSpecDecl>(D) ||
- isa<NamespaceDecl>(D)) {
+ isa<NamespaceDecl>(D) || isa<ExportDecl>(D)) {
// These decls cover a lot of nested declarations that might not be used,
// reducing the granularity and making the output less useful.
return;
}
- auto *DC = D->getLexicalDeclContext();
- if (!DC || !DC->isFileContext()) {
- // We choose to work at namespace level to reduce complexity and the
- // number of cases we care about.
+ if (isa<ParmVarDecl>(D)) {
+ // Parameters are covered by their functions.
return;
}
+ auto *DC = D->getLexicalDeclContext();
+ if (!DC || !shouldIncludeDeclsIn(DC))
+ return;
PendingDecls.push_back(D);
- if (auto *NS = dyn_cast<NamespaceDecl>(DC)) {
- // Add any namespaces we have not seen before.
- // Note that we filter out namespaces from DeclRead as it includes too
- // all redeclarations and we only want the ones that had other used
- // declarations.
- while (NS && ProcessedNamespaces.insert(NS).second) {
- PendingDecls.push_back(NS);
-
- NS = dyn_cast<NamespaceDecl>(NS->getLexicalParent());
- }
+ for (; (isa<ExportDecl>(DC) || isa<NamespaceDecl>(DC)) &&
+ ProcessedDeclContexts.insert(DC).second;
+ DC = DC->getLexicalParent()) {
+ // Add any interesting decl contexts that we have not seen before.
+ // Note that we filter them out from DeclRead as that would include all
+ // redeclarations of namespaces, potentially those that do not have any
+ // imported declarations.
+ PendingDecls.push_back(cast<Decl>(DC));
}
}
@@ -205,12 +204,38 @@ public:
private:
std::vector<const Decl *> PendingDecls;
- llvm::SmallPtrSet<const NamespaceDecl *, 0> ProcessedNamespaces;
+ llvm::SmallPtrSet<const DeclContext *, 0> ProcessedDeclContexts;
bool IsCollectingDecls = true;
const SourceManager &SM;
std::unique_ptr<llvm::raw_ostream> OS;
+ static bool shouldIncludeDeclsIn(const DeclContext *DC) {
+ assert(DC && "DC is null");
+ // We choose to work at namespace level to reduce complexity and the number
+ // of cases we care about.
+ // We still need to carefully handle composite declarations like
+ // `ExportDecl`.
+ for (; DC; DC = DC->getLexicalParent()) {
+ if (DC->isFileContext())
+ return true;
+ if (isa<ExportDecl>(DC))
+ continue; // Depends on the parent.
+ return false;
+ }
+ llvm_unreachable("DeclContext chain must end with a translation unit");
+ }
+
llvm::SmallVector<CharSourceRange, 2> getRangesToMark(const Decl *D) {
+ if (auto *ED = dyn_cast<ExportDecl>(D)) {
+ if (!ED->hasBraces())
+ return {SM.getExpansionRange(ED->getExportLoc())};
+
+ return {SM.getExpansionRange(SourceRange(
+ ED->getExportLoc(),
+ lexForLBrace(ED->getExportLoc(), D->getLangOpts()))),
+ SM.getExpansionRange(ED->getRBraceLoc())};
+ }
+
auto *NS = dyn_cast<NamespaceDecl>(D);
if (!NS)
return {SM.getExpansionRange(D->getSourceRange())};
@@ -232,17 +257,7 @@ private:
}
}
}
- auto &LangOpts = D->getLangOpts();
- // Now skip one token, the next should be the lbrace.
- Token Tok;
- if (Lexer::getRawToken(TokenBeforeLBrace, Tok, SM, LangOpts, true) ||
- Lexer::getRawToken(Tok.getEndLoc(), Tok, SM, LangOpts, true) ||
- Tok.getKind() != tok::l_brace) {
- // On error or if we did not find the token we expected, avoid marking
- // everything inside the namespace as used.
- return {};
- }
- LBraceLoc = Tok.getLocation();
+ LBraceLoc = lexForLBrace(TokenBeforeLBrace, D->getLangOpts());
}
return {SM.getExpansionRange(SourceRange(NS->getBeginLoc(), LBraceLoc)),
SM.getExpansionRange(NS->getRBraceLoc())};
@@ -285,6 +300,20 @@ private:
OS->flush();
}
+
+ SourceLocation lexForLBrace(SourceLocation TokenBeforeLBrace,
+ const LangOptions &LangOpts) {
+ // Now skip one token, the next should be the lbrace.
+ Token Tok;
+ if (Lexer::getRawToken(TokenBeforeLBrace, Tok, SM, LangOpts, true) ||
+ Lexer::getRawToken(Tok.getEndLoc(), Tok, SM, LangOpts, true) ||
+ Tok.getKind() != tok::l_brace) {
+ // On error or if we did not find the token we expected, avoid marking
+ // everything inside the namespace as used.
+ return SourceLocation();
+ }
+ return Tok.getLocation();
+ }
};
/// Dumps deserialized declarations.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e47caeb..5fb3f92 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3310,6 +3310,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
case NameClassificationKind::TypeTemplate:
case NameClassificationKind::UndeclaredNonType:
case NameClassificationKind::UndeclaredTemplate:
+ case NameClassificationKind::Concept:
// Not a previously-declared non-type entity.
MightBeDeclarator = false;
break;
@@ -3320,7 +3321,6 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
case NameClassificationKind::OverloadSet:
case NameClassificationKind::VarTemplate:
case NameClassificationKind::FunctionTemplate:
- case NameClassificationKind::Concept:
// Might be a redeclaration of a prior entity.
break;
}
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 9a04bf2..0277dfb 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -711,41 +711,61 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
}
}
- // Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
- // Generate a meaningful error if the user forgot to put class before the
- // identifier, comma, or greater. Provide a fixit if the identifier, comma,
- // or greater appear immediately or after 'struct'. In the latter case,
- // replace the keyword with 'class'.
+ TemplateNameKind Kind = TemplateNameKind::TNK_Non_template;
+ SourceLocation NameLoc;
+ IdentifierInfo *ParamName = nullptr;
+ SourceLocation EllipsisLoc;
bool TypenameKeyword = false;
- if (!TryConsumeToken(tok::kw_class)) {
+
+ if (TryConsumeToken(tok::kw_class)) {
+ Kind = TemplateNameKind::TNK_Type_template;
+ } else {
+
+ // Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
+ // Generate a meaningful error if the user forgot to put class before the
+ // identifier, comma, or greater. Provide a fixit if the identifier, comma,
+ // or greater appear immediately or after 'struct'. In the latter case,
+ // replace the keyword with 'class'.
bool Replace = Tok.isOneOf(tok::kw_typename, tok::kw_struct);
const Token &Next = Tok.is(tok::kw_struct) ? NextToken() : Tok;
if (Tok.is(tok::kw_typename)) {
TypenameKeyword = true;
+ Kind = TemplateNameKind::TNK_Type_template;
Diag(Tok.getLocation(),
getLangOpts().CPlusPlus17
? diag::warn_cxx14_compat_template_template_param_typename
: diag::ext_template_template_param_typename)
- << (!getLangOpts().CPlusPlus17
- ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
- : FixItHint());
+ << (!getLangOpts().CPlusPlus17
+ ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
+ : FixItHint());
+ Kind = TemplateNameKind::TNK_Type_template;
+ } else if (TryConsumeToken(tok::kw_concept)) {
+ Kind = TemplateNameKind::TNK_Concept_template;
+ } else if (TryConsumeToken(tok::kw_auto)) {
+ Kind = TemplateNameKind::TNK_Var_template;
} else if (Next.isOneOf(tok::identifier, tok::comma, tok::greater,
tok::greatergreater, tok::ellipsis)) {
+ // Provide a fixit if the identifier, comma,
+ // or greater appear immediately or after 'struct'. In the latter case,
+ // replace the keyword with 'class'.
Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
<< getLangOpts().CPlusPlus17
<< (Replace
? FixItHint::CreateReplacement(Tok.getLocation(), "class")
: FixItHint::CreateInsertion(Tok.getLocation(), "class "));
- } else
- Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
- << getLangOpts().CPlusPlus17;
-
+ }
if (Replace)
ConsumeToken();
}
+ if (!getLangOpts().CPlusPlus26 &&
+ (Kind == TemplateNameKind::TNK_Concept_template ||
+ Kind == TemplateNameKind::TNK_Var_template)) {
+ Diag(PrevTokLocation, diag::err_cxx26_template_template_params)
+ << (Kind == TemplateNameKind::TNK_Concept_template);
+ }
+
// Parse the ellipsis, if given.
- SourceLocation EllipsisLoc;
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
Diag(EllipsisLoc,
getLangOpts().CPlusPlus11
@@ -753,8 +773,7 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
: diag::ext_variadic_templates);
// Get the identifier, if given.
- SourceLocation NameLoc = Tok.getLocation();
- IdentifierInfo *ParamName = nullptr;
+ NameLoc = Tok.getLocation();
if (Tok.is(tok::identifier)) {
ParamName = Tok.getIdentifierInfo();
ConsumeToken();
@@ -792,7 +811,7 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
}
return Actions.ActOnTemplateTemplateParameter(
- getCurScope(), TemplateLoc, ParamList, TypenameKeyword, EllipsisLoc,
+ getCurScope(), TemplateLoc, Kind, TypenameKeyword, ParamList, EllipsisLoc,
ParamName, NameLoc, Depth, Position, EqualLoc, DefaultArg);
}
@@ -1201,7 +1220,8 @@ static bool isEndOfTemplateArgument(Token Tok) {
ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) &&
- !Tok.is(tok::annot_cxxscope))
+ !Tok.is(tok::annot_cxxscope) && !Tok.is(tok::annot_template_id) &&
+ !Tok.is(tok::annot_non_type))
return ParsedTemplateArgument();
// C++0x [temp.arg.template]p1:
@@ -1245,12 +1265,27 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
/*EnteringContext=*/false, Template))
Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
}
- } else if (Tok.is(tok::identifier)) {
+ } else if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) ||
+ Tok.is(tok::annot_non_type)) {
// We may have a (non-dependent) template name.
TemplateTy Template;
UnqualifiedId Name;
- Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
- ConsumeToken(); // the identifier
+ if (Tok.is(tok::annot_non_type)) {
+ NamedDecl *ND = getNonTypeAnnotation(Tok);
+ if (!isa<VarTemplateDecl>(ND))
+ return Result;
+ Name.setIdentifier(ND->getIdentifier(), Tok.getLocation());
+ ConsumeAnnotationToken();
+ } else if (Tok.is(tok::annot_template_id)) {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ if (TemplateId->LAngleLoc.isValid())
+ return Result;
+ Name.setIdentifier(TemplateId->Name, Tok.getLocation());
+ ConsumeAnnotationToken();
+ } else {
+ Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ ConsumeToken(); // the identifier
+ }
TryConsumeToken(tok::ellipsis, EllipsisLoc);
@@ -1261,7 +1296,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
/*hasTemplateKeyword=*/false, Name,
/*ObjectType=*/nullptr,
/*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
- if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template) {
+ if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template ||
+ TNK == TNK_Var_template || TNK == TNK_Concept_template) {
// We have an id-expression that refers to a class template or
// (C++0x) alias template.
Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
@@ -1301,13 +1337,12 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
{
TentativeParsingAction TPA(*this);
- ParsedTemplateArgument TemplateTemplateArgument
- = ParseTemplateTemplateArgument();
+ ParsedTemplateArgument TemplateTemplateArgument =
+ ParseTemplateTemplateArgument();
if (!TemplateTemplateArgument.isInvalid()) {
TPA.Commit();
return TemplateTemplateArgument;
}
-
// Revert this tentative parse to parse a non-type template argument.
TPA.Revert();
}
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index b58100c..2a731a1 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1376,7 +1376,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
// If we annotated then the current token should not still be ::
// FIXME we may want to also check for tok::annot_typename but
// currently don't have a test case.
- if (Tok.isNot(tok::annot_cxxscope))
+ if (Tok.isNot(tok::annot_cxxscope) && Tok.isNot(tok::identifier))
break;
}
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index ff50b3f..a8cfe20 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1838,7 +1838,8 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
case NameClassificationKind::TypeTemplate:
if (Next.isNot(tok::less)) {
- // This may be a type template being used as a template template argument.
+ // This may be a type or variable template being used as a template
+ // template argument.
if (SS.isNotEmpty())
AnnotateScopeToken(SS, !WasScopeAnnotation);
return AnnotatedNameKind::TemplateName;
@@ -1852,10 +1853,10 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
Classification.getKind() == NameClassificationKind::Concept;
// We have a template name followed by '<'. Consume the identifier token so
// we reach the '<' and annotate it.
- if (Next.is(tok::less))
- ConsumeToken();
UnqualifiedId Id;
Id.setIdentifier(Name, NameLoc);
+ if (Next.is(tok::less))
+ ConsumeToken();
if (AnnotateTemplateIdToken(
TemplateTy::make(Classification.getTemplateName()),
Classification.getTemplateNameKind(), SS, SourceLocation(), Id,
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 924becf..cfb2f60 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -443,9 +443,7 @@ void Sema::Initialize() {
if (getLangOpts().MSVCCompat) {
if (getLangOpts().CPlusPlus &&
IdResolver.begin(&Context.Idents.get("type_info")) == IdResolver.end())
- PushOnScopeChains(
- Context.buildImplicitRecord("type_info", TagTypeKind::Class),
- TUScope);
+ PushOnScopeChains(Context.getMSTypeInfoTagDecl(), TUScope);
addImplicitTypedef("size_t", Context.getSizeType());
}
diff --git a/clang/lib/Sema/SemaAMDGPU.cpp b/clang/lib/Sema/SemaAMDGPU.cpp
index a5fbd70..1913bb8 100644
--- a/clang/lib/Sema/SemaAMDGPU.cpp
+++ b/clang/lib/Sema/SemaAMDGPU.cpp
@@ -82,7 +82,7 @@ bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
return checkMovDPPFunctionCall(TheCall, 5, 1);
case AMDGPU::BI__builtin_amdgcn_mov_dpp8:
return checkMovDPPFunctionCall(TheCall, 2, 1);
- case AMDGPU::BI__builtin_amdgcn_update_dpp: {
+ case AMDGPU::BI__builtin_amdgcn_update_dpp:
return checkMovDPPFunctionCall(TheCall, 6, 2);
case AMDGPU::BI__builtin_amdgcn_cvt_scale_pk8_f16_fp8:
case AMDGPU::BI__builtin_amdgcn_cvt_scale_pk8_bf16_fp8:
@@ -100,7 +100,6 @@ bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
case AMDGPU::BI__builtin_amdgcn_cvt_scale_pk16_f32_fp6:
case AMDGPU::BI__builtin_amdgcn_cvt_scale_pk16_f32_bf6:
return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 7);
- }
default:
return false;
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index bc87611..3c4511b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -35,6 +35,7 @@
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TemplateBase.h"
+#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/UnresolvedSet.h"
@@ -3716,7 +3717,7 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) {
- if (ConceptDecl *Decl = AutoT->getTypeConstraintConcept()) {
+ if (TemplateDecl *Decl = AutoT->getTypeConstraintConcept()) {
DiagnoseUseOfDecl(Decl, Loc);
}
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3eaf2eb..b5eb825 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3269,11 +3269,10 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
continue;
if (isa<InferredNoReturnAttr>(I)) {
- if (auto *FD = dyn_cast<FunctionDecl>(New)) {
- if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
- continue; // Don't propagate inferred noreturn attributes to explicit
- // specializations.
- }
+ if (auto *FD = dyn_cast<FunctionDecl>(New);
+ FD &&
+ FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+ continue; // Don't propagate inferred noreturn attributes to explicit
}
if (mergeDeclAttribute(*this, New, I, LocalAMK))
@@ -8113,9 +8112,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
}
}
- NewVD->addAttr(AsmLabelAttr::Create(Context, Label,
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewVD->addAttr(AsmLabelAttr::Create(Context, Label, SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewVD->getIdentifier());
@@ -10345,9 +10342,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (Expr *E = D.getAsmLabel()) {
// The parser guarantees this is a string.
StringLiteral *SE = cast<StringLiteral>(E);
- NewFD->addAttr(AsmLabelAttr::Create(Context, SE->getString(),
- /*IsLiteralLabel=*/true,
- SE->getStrTokenLoc(0)));
+ NewFD->addAttr(
+ AsmLabelAttr::Create(Context, SE->getString(), SE->getStrTokenLoc(0)));
} else if (!ExtnameUndeclaredIdentifiers.empty()) {
llvm::DenseMap<IdentifierInfo*,AsmLabelAttr*>::iterator I =
ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
@@ -20598,8 +20594,8 @@ void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name,
LookupOrdinaryName);
AttributeCommonInfo Info(AliasName, SourceRange(AliasNameLoc),
AttributeCommonInfo::Form::Pragma());
- AsmLabelAttr *Attr = AsmLabelAttr::CreateImplicit(
- Context, AliasName->getName(), /*IsLiteralLabel=*/true, Info);
+ AsmLabelAttr *Attr =
+ AsmLabelAttr::CreateImplicit(Context, AliasName->getName(), Info);
// If a declaration that:
// 1) declares a function or a variable
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e4c2543..6793d6d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2903,8 +2903,9 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
// in BuildTemplateIdExpr().
// The single lookup result must be a variable template declaration.
if (Id.getKind() == UnqualifiedIdKind::IK_TemplateId && Id.TemplateId &&
- Id.TemplateId->Kind == TNK_Var_template) {
- assert(R.getAsSingle<VarTemplateDecl>() &&
+ (Id.TemplateId->Kind == TNK_Var_template ||
+ Id.TemplateId->Kind == TNK_Concept_template)) {
+ assert(R.getAsSingle<TemplateDecl>() &&
"There should only be one declaration found.");
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 7b9c638..1dd38c0 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -3713,7 +3713,7 @@ ValueDecl *InitializedEntity::getDecl() const {
case EK_ParenAggInitMember:
case EK_Binding:
case EK_TemplateParameter:
- return Variable.VariableOrMember;
+ return cast<ValueDecl>(Variable.VariableOrMember);
case EK_Parameter:
case EK_Parameter_CF_Audited:
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 8bfea62..8212646 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -17,6 +17,7 @@
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/Initialization.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/StringExtras.h"
@@ -641,21 +642,13 @@ ExprResult CheckVarType(SemaOpenACC &S, OpenACCClauseKind CK, Expr *VarExpr,
if (!InnerExpr || InnerExpr->isTypeDependent())
return VarExpr;
- const auto *RD = InnerExpr->getType()->getAsCXXRecordDecl();
+ auto *RD = InnerExpr->getType()->getAsCXXRecordDecl();
// if this isn't a C++ record decl, we can create/copy/destroy this thing at
// will without problem, so this is a success.
if (!RD)
return VarExpr;
- // TODO: OpenACC:
- // Private must have default ctor + dtor in InnerExpr
- // FirstPrivate must have copyctor + dtor in InnerExpr
- // Reduction must have copyctor + dtor + operation in InnerExpr
-
- // TODO OpenACC: It isn't clear what the requirements are for default
- // constructor/copy constructor are for First private and reduction, but
- // private requires a default constructor.
if (CK == OpenACCClauseKind::Private) {
bool HasNonDeletedDefaultCtor =
llvm::find_if(RD->ctors(), [](const CXXConstructorDecl *CD) {
@@ -668,6 +661,26 @@ ExprResult CheckVarType(SemaOpenACC &S, OpenACCClauseKind CK, Expr *VarExpr,
<< clang::diag::AccVarReferencedReason::DefCtor;
return ExprError();
}
+ } else if (CK == OpenACCClauseKind::FirstPrivate) {
+ if (!RD->hasSimpleCopyConstructor()) {
+ Sema::SpecialMemberOverloadResult SMOR = S.SemaRef.LookupSpecialMember(
+ RD, CXXSpecialMemberKind::CopyConstructor, /*ConstArg=*/true,
+ /*VolatileArg=*/false, /*RValueThis=*/false, /*ConstThis=*/false,
+ /*VolatileThis=*/false);
+
+ if (SMOR.getKind() != Sema::SpecialMemberOverloadResult::Success ||
+ SMOR.getMethod()->isDeleted()) {
+ S.Diag(InnerExpr->getBeginLoc(),
+ clang::diag::warn_acc_var_referenced_lacks_op)
+ << InnerExpr->getType() << CK
+ << clang::diag::AccVarReferencedReason::CopyCtor;
+ return ExprError();
+ }
+ }
+ } else if (CK == OpenACCClauseKind::Reduction) {
+ // TODO: OpenACC:
+ // Reduction must have copyctor + dtor + operation in InnerExpr I think?
+ // Need to confirm when implementing this part.
}
// All 3 things need to make sure they have a dtor.
@@ -2552,3 +2565,50 @@ ExprResult
SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) {
return BuildOpenACCAsteriskSizeExpr(AsteriskLoc);
}
+
+VarDecl *SemaOpenACC::CreateInitRecipe(const Expr *VarExpr) {
+ // Strip off any array subscripts/array section exprs to get to the type of
+ // the variable.
+ while (isa_and_present<ArraySectionExpr, ArraySubscriptExpr>(VarExpr)) {
+ if (const auto *AS = dyn_cast<ArraySectionExpr>(VarExpr))
+ VarExpr = AS->getBase()->IgnoreParenImpCasts();
+ else if (const auto *Sub = dyn_cast<ArraySubscriptExpr>(VarExpr))
+ VarExpr = Sub->getBase()->IgnoreParenImpCasts();
+ }
+
+ // If for some reason the expression is invalid, or this is dependent, just
+ // fill in with nullptr. We'll count on TreeTransform to make this if
+ // necessary.
+ if (!VarExpr || VarExpr->getType()->isDependentType())
+ return nullptr;
+
+ QualType VarTy =
+ VarExpr->getType().getNonReferenceType().getUnqualifiedType();
+
+ VarDecl *Recipe = VarDecl::Create(
+ getASTContext(), SemaRef.getCurContext(), VarExpr->getBeginLoc(),
+ VarExpr->getBeginLoc(),
+ &getASTContext().Idents.get("openacc.private.init"), VarTy,
+ getASTContext().getTrivialTypeSourceInfo(VarTy), SC_Auto);
+
+ ExprResult Init;
+
+ {
+ // Trap errors so we don't get weird ones here. If we can't init, we'll just
+ // swallow the errors.
+ Sema::TentativeAnalysisScope Trap{SemaRef};
+ InitializedEntity Entity = InitializedEntity::InitializeVariable(Recipe);
+ InitializationKind Kind =
+ InitializationKind::CreateDefault(Recipe->getLocation());
+
+ InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, {});
+ Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, {});
+ }
+
+ if (Init.get()) {
+ Recipe->setInit(Init.get());
+ Recipe->setInitStyle(VarDecl::CallInit);
+ }
+
+ return Recipe;
+}
diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp
index b54a012..9f1a617 100644
--- a/clang/lib/Sema/SemaOpenACCClause.cpp
+++ b/clang/lib/Sema/SemaOpenACCClause.cpp
@@ -795,9 +795,15 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitPrivateClause(
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
- return OpenACCPrivateClause::Create(Ctx, Clause.getBeginLoc(),
- Clause.getLParenLoc(),
- Clause.getVarList(), Clause.getEndLoc());
+ llvm::SmallVector<VarDecl *> InitRecipes;
+
+ // Assemble the recipes list.
+ for (const Expr *VarExpr : Clause.getVarList())
+ InitRecipes.push_back(SemaRef.CreateInitRecipe(VarExpr));
+
+ return OpenACCPrivateClause::Create(
+ Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
+ InitRecipes, Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause(
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index 994cd07..7b16d08 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -1552,9 +1552,11 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
HasSiFiveCLICType = true;
break;
case RISCVInterruptAttr::supervisor:
+ case RISCVInterruptAttr::rnmi:
case RISCVInterruptAttr::qcinest:
case RISCVInterruptAttr::qcinonest:
- // "supervisor" and "qci-(no)nest" cannot be combined with any other types
+ // "supervisor", "rnmi" and "qci-(no)nest" cannot be combined with any
+ // other types
HasUnaryType = true;
break;
}
@@ -1608,6 +1610,14 @@ void SemaRISCV::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
return;
}
} break;
+ case RISCVInterruptAttr::rnmi: {
+ if (!HasFeature("smrnmi")) {
+ Diag(AL.getLoc(),
+ diag::err_riscv_attribute_interrupt_requires_extension)
+ << RISCVInterruptAttr::ConvertInterruptTypeToStr(Type) << "Smrnmi";
+ return;
+ }
+ } break;
default:
break;
}
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 3f89843..a5f9202 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2284,7 +2284,11 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
// we can diagnose if we don't see any variable declarations. This
// covers a case like declaring a typedef, function, or structure
// type rather than a variable.
- NonVarSeen = DI;
+ //
+ // Note, _Static_assert is acceptable because it does not declare an
+ // identifier at all, so "for object having" does not apply.
+ if (!isa<StaticAssertDecl>(DI))
+ NonVarSeen = DI;
}
}
// Diagnose if we saw a non-variable declaration but no variable
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 21fed2e..b6b8932 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
@@ -38,6 +39,7 @@
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/SaveAndRestore.h"
#include <optional>
@@ -306,9 +308,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD) ||
isa<BuiltinTemplateDecl>(TD) || isa<ConceptDecl>(TD));
TemplateKind =
- isa<VarTemplateDecl>(TD) ? TNK_Var_template :
- isa<ConceptDecl>(TD) ? TNK_Concept_template :
- TNK_Type_template;
+ isa<TemplateTemplateParmDecl>(TD)
+ ? dyn_cast<TemplateTemplateParmDecl>(TD)->templateParameterKind()
+ : isa<VarTemplateDecl>(TD) ? TNK_Var_template
+ : isa<ConceptDecl>(TD) ? TNK_Concept_template
+ : TNK_Type_template;
}
}
@@ -1071,12 +1075,26 @@ makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) {
bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
TemplateName TN = TypeConstr->Template.get();
- ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+ NamedDecl *CD = nullptr;
+ bool IsTypeConcept = false;
+ bool RequiresArguments = false;
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
+ IsTypeConcept = TTP->isTypeConceptTemplateParam();
+ RequiresArguments =
+ TTP->getTemplateParameters()->getMinRequiredArguments() > 1;
+ CD = TTP;
+ } else {
+ CD = TN.getAsTemplateDecl();
+ IsTypeConcept = cast<ConceptDecl>(CD)->isTypeConcept();
+ RequiresArguments = cast<ConceptDecl>(CD)
+ ->getTemplateParameters()
+ ->getMinRequiredArguments() > 1;
+ }
// C++2a [temp.param]p4:
// [...] The concept designated by a type-constraint shall be a type
// concept ([temp.concept]).
- if (!CD->isTypeConcept()) {
+ if (!IsTypeConcept) {
Diag(TypeConstr->TemplateNameLoc,
diag::err_type_constraint_non_type_concept);
return true;
@@ -1087,8 +1105,7 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
- if (!WereArgsSpecified &&
- CD->getTemplateParameters()->getMinRequiredArguments() > 1) {
+ if (!WereArgsSpecified && RequiresArguments) {
Diag(TypeConstr->TemplateNameLoc,
diag::err_type_constraint_missing_arguments)
<< CD;
@@ -1115,7 +1132,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
return true;
TemplateName TN = TypeConstr->Template.get();
- ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+ TemplateDecl *CD = cast<TemplateDecl>(TN.getAsTemplateDecl());
UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
@@ -1143,7 +1160,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
template <typename ArgumentLocAppender>
static ExprResult formImmediatelyDeclaredConstraint(
Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo,
- ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
+ NamedDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
SourceLocation RAngleLoc, QualType ConstrainedType,
SourceLocation ParamNameLoc, ArgumentLocAppender Appender,
SourceLocation EllipsisLoc) {
@@ -1162,10 +1179,19 @@ static ExprResult formImmediatelyDeclaredConstraint(
// constraint of T. [...]
CXXScopeSpec SS;
SS.Adopt(NS);
- ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
- SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
- /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
- &ConstraintArgs);
+ ExprResult ImmediatelyDeclaredConstraint;
+ if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
+ ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
+ SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+ /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
+ &ConstraintArgs);
+ }
+ // We have a template template parameter
+ else {
+ auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
+ ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
+ SS, NameInfo, CDT, SourceLocation(), &ConstraintArgs);
+ }
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
@@ -1191,7 +1217,8 @@ static ExprResult formImmediatelyDeclaredConstraint(
bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
- ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
+ TemplateDecl *NamedConcept,
+ NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc) {
@@ -1588,11 +1615,15 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
return Param;
}
+/// ActOnTemplateTemplateParameter - Called when a C++ template template
+/// parameter (e.g. T in template <template \<typename> class T> class array)
+/// has been parsed. S is the current scope.
NamedDecl *Sema::ActOnTemplateTemplateParameter(
- Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params,
- bool Typename, SourceLocation EllipsisLoc, IdentifierInfo *Name,
- SourceLocation NameLoc, unsigned Depth, unsigned Position,
- SourceLocation EqualLoc, ParsedTemplateArgument Default) {
+ Scope *S, SourceLocation TmpLoc, TemplateNameKind Kind, bool Typename,
+ TemplateParameterList *Params, SourceLocation EllipsisLoc,
+ IdentifierInfo *Name, SourceLocation NameLoc, unsigned Depth,
+ unsigned Position, SourceLocation EqualLoc,
+ ParsedTemplateArgument Default) {
assert(S->isTemplateParamScope() &&
"Template template parameter not in template parameter scope!");
@@ -1609,7 +1640,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
Context, Context.getTranslationUnitDecl(),
NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
- Name, Typename, Params);
+ Name, Kind, Typename, Params);
Param->setAccess(AS_public);
if (Param->isParameterPack())
@@ -1658,6 +1689,14 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
return Param;
}
+ TemplateName Name =
+ DefaultArg.getArgument().getAsTemplateOrTemplatePattern();
+ TemplateDecl *Template = Name.getAsTemplateDecl();
+ if (Template &&
+ !CheckDeclCompatibleWithTemplateTemplate(Template, Param, DefaultArg)) {
+ return Param;
+ }
+
// Check for unexpanded parameter packs.
if (DiagnoseUnexpandedParameterPack(DefaultArg.getLocation(),
DefaultArg.getArgument().getAsTemplate(),
@@ -2667,6 +2706,18 @@ struct DependencyChecker : DynamicRecursiveASTVisitor {
return DynamicRecursiveASTVisitor::VisitDeclRefExpr(E);
}
+ bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) override {
+ if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
+ if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+ if (Matches(TTP->getDepth(), ULE->getExprLoc()))
+ return false;
+ }
+ for (auto &TLoc : ULE->template_arguments())
+ DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
+ }
+ return DynamicRecursiveASTVisitor::VisitUnresolvedLookupExpr(ULE);
+ }
+
bool VisitSubstTemplateTypeParmType(SubstTemplateTypeParmType *T) override {
return TraverseType(T->getReplacementType());
}
@@ -4047,8 +4098,10 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized,
static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
-static bool isTemplateArgumentTemplateParameter(
- const TemplateArgument &Arg, unsigned Depth, unsigned Index) {
+static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
+ NamedDecl *Param,
+ unsigned Depth,
+ unsigned Index) {
switch (Arg.getKind()) {
case TemplateArgument::Null:
case TemplateArgument::NullPtr:
@@ -4104,7 +4157,8 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
Arg = Arg.pack_begin()->getPackExpansionPattern();
}
- if (!isTemplateArgumentTemplateParameter(Arg, Depth, I))
+ if (!isTemplateArgumentTemplateParameter(Arg, Params->getParam(I), Depth,
+ I))
return false;
}
@@ -4694,6 +4748,43 @@ ExprResult Sema::CheckVarTemplateId(
return BuildDeclarationNameExpr(SS, NameInfo, Var, FoundD, TemplateArgs);
}
+ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
+ const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
+ TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+ const TemplateArgumentListInfo *TemplateArgs) {
+ assert(Template && "A variable template id without template?");
+
+ if (Template->templateParameterKind() != TemplateNameKind::TNK_Var_template &&
+ Template->templateParameterKind() !=
+ TemplateNameKind::TNK_Concept_template)
+ return ExprResult();
+
+ // Check that the template argument list is well-formed for this template.
+ CheckTemplateArgumentInfo CTAI;
+ if (CheckTemplateArgumentList(
+ Template, TemplateLoc,
+ // FIXME: TemplateArgs will not be modified because
+ // UpdateArgsWithConversions is false, however, we should
+ // CheckTemplateArgumentList to be const-correct.
+ const_cast<TemplateArgumentListInfo &>(*TemplateArgs),
+ /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI,
+ /*UpdateArgsWithConversions=*/false))
+ return true;
+
+ UnresolvedSet<1> R;
+ R.addDecl(Template);
+
+ // FIXME: We model references to variable template and concept parameters
+ // as an UnresolvedLookupExpr. This is because they encapsulate the same
+ // data, can generally be used in the same places and work the same way.
+ // However, it might be cleaner to use a dedicated AST node in the long run.
+ return UnresolvedLookupExpr::Create(
+ getASTContext(), nullptr, SS.getWithLocInContext(getASTContext()),
+ SourceLocation(), NameInfo, false, TemplateArgs, R.begin(), R.end(),
+ /*KnownDependent=*/false,
+ /*KnownInstantiationDependent=*/false);
+}
+
void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
SourceLocation Loc) {
Diag(Loc, diag::err_template_missing_args)
@@ -4804,21 +4895,29 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
KnownDependent = true;
}
+ // We don't want lookup warnings at this point.
+ R.suppressDiagnostics();
+
if (R.getAsSingle<ConceptDecl>()) {
return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
R.getRepresentativeDecl(),
R.getAsSingle<ConceptDecl>(), TemplateArgs);
}
- // We don't want lookup warnings at this point.
- R.suppressDiagnostics();
+ // Check variable template ids (C++17) and concept template parameters
+ // (C++26).
+ UnresolvedLookupExpr *ULE;
+ if (R.getAsSingle<TemplateTemplateParmDecl>())
+ return CheckVarOrConceptTemplateTemplateId(
+ SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
+ TemplateKWLoc, TemplateArgs);
- UnresolvedLookupExpr *ULE = UnresolvedLookupExpr::Create(
+ // Function templates
+ ULE = UnresolvedLookupExpr::Create(
Context, R.getNamingClass(), SS.getWithLocInContext(Context),
TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
R.begin(), R.end(), KnownDependent,
/*KnownInstantiationDependent=*/false);
-
// Model the templates with UnresolvedTemplateTy. The expression should then
// either be transformed in an instantiation or be diagnosed in
// CheckPlaceholderExpr.
@@ -5609,12 +5708,25 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
break;
case TemplateArgument::Expression:
- case TemplateArgument::Type:
+ case TemplateArgument::Type: {
+ auto Kind = 0;
+ switch (TempParm->templateParameterKind()) {
+ case TemplateNameKind::TNK_Var_template:
+ Kind = 1;
+ break;
+ case TemplateNameKind::TNK_Concept_template:
+ Kind = 2;
+ break;
+ default:
+ break;
+ }
+
// We have a template template parameter but the template
// argument does not refer to a template.
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_template)
- << getLangOpts().CPlusPlus11;
+ << Kind << getLangOpts().CPlusPlus11;
return true;
+ }
case TemplateArgument::Declaration:
case TemplateArgument::Integral:
@@ -6359,7 +6471,7 @@ enum NullPointerValueKind {
/// Determine whether the given template argument is a null pointer
/// value of the appropriate type.
static NullPointerValueKind
-isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
+isNullPointerValueTemplateArgument(Sema &S, NamedDecl *Param,
QualType ParamType, Expr *Arg,
Decl *Entity = nullptr) {
if (Arg->isValueDependent() || Arg->isTypeDependent())
@@ -6464,9 +6576,10 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
/// Checks whether the given template argument is compatible with its
/// template parameter.
-static bool CheckTemplateArgumentIsCompatibleWithParameter(
- Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
- Expr *Arg, QualType ArgType) {
+static bool
+CheckTemplateArgumentIsCompatibleWithParameter(Sema &S, NamedDecl *Param,
+ QualType ParamType, Expr *ArgIn,
+ Expr *Arg, QualType ArgType) {
bool ObjCLifetimeConversion;
if (ParamType->isPointerType() &&
!ParamType->castAs<PointerType>()->getPointeeType()->isFunctionType() &&
@@ -6522,7 +6635,7 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter(
/// Checks whether the given template argument is the address
/// of an object or function according to C++ [temp.arg.nontype]p1.
static bool CheckTemplateArgumentAddressOfObjectOrFunction(
- Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
+ Sema &S, NamedDecl *Param, QualType ParamType, Expr *ArgIn,
TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
bool Invalid = false;
Expr *Arg = ArgIn;
@@ -6784,11 +6897,9 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
/// Checks whether the given template argument is a pointer to
/// member constant according to C++ [temp.arg.nontype]p1.
-static bool
-CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
- QualType ParamType, Expr *&ResultArg,
- TemplateArgument &SugaredConverted,
- TemplateArgument &CanonicalConverted) {
+static bool CheckTemplateArgumentPointerToMember(
+ Sema &S, NamedDecl *Param, QualType ParamType, Expr *&ResultArg,
+ TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
bool Invalid = false;
Expr *Arg = ResultArg;
@@ -6919,8 +7030,15 @@ CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
return true;
}
-ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
- QualType ParamType, Expr *Arg,
+/// Check a template argument against its corresponding
+/// non-type template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.nontype].
+/// If an error occurred, it returns ExprError(); otherwise, it
+/// returns the converted template argument. \p ParamType is the
+/// type of the non-type template parameter after it has been instantiated.
+ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
+ Expr *Arg,
TemplateArgument &SugaredConverted,
TemplateArgument &CanonicalConverted,
bool StrictCheck,
@@ -6974,7 +7092,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ExprError();
} else {
TemplateDeductionInfo Info(DeductionArg->getExprLoc(),
- Param->getDepth() + 1);
+ Param->getTemplateDepth() + 1);
ParamType = QualType();
TemplateDeductionResult Result =
DeduceAutoType(TSI->getTypeLoc(), DeductionArg, ParamType, Info,
@@ -6986,13 +7104,14 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
// checking the template argument list.
/*IgnoreConstraints=*/true);
if (Result == TemplateDeductionResult::AlreadyDiagnosed) {
- if (ParamType.isNull())
- return ExprError();
+ return ExprError();
} else if (Result != TemplateDeductionResult::Success) {
- Diag(Arg->getExprLoc(),
- diag::err_non_type_template_parm_type_deduction_failure)
- << Param->getDeclName() << Param->getType() << Arg->getType()
- << Arg->getSourceRange();
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ Diag(Arg->getExprLoc(),
+ diag::err_non_type_template_parm_type_deduction_failure)
+ << Param->getDeclName() << NTTP->getType() << Arg->getType()
+ << Arg->getSourceRange();
+ }
NoteTemplateParameterLocation(*Param);
return ExprError();
}
@@ -7387,7 +7506,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (IntegerType->isUnsignedIntegerOrEnumerationType() &&
(OldValue.isSigned() && OldValue.isNegative())) {
Diag(Arg->getBeginLoc(), diag::warn_template_arg_negative)
- << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+ << toString(OldValue, 10) << toString(Value, 10) << ParamType
<< Arg->getSourceRange();
NoteTemplateParameterLocation(*Param);
}
@@ -7402,7 +7521,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
RequiredBits = OldValue.getSignificantBits();
if (RequiredBits > AllowedBits) {
Diag(Arg->getBeginLoc(), diag::warn_template_arg_too_large)
- << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+ << toString(OldValue, 10) << toString(Value, 10) << ParamType
<< Arg->getSourceRange();
NoteTemplateParameterLocation(*Param);
}
@@ -7581,6 +7700,81 @@ static void DiagnoseTemplateParameterListArityMismatch(
Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
+bool Sema::CheckDeclCompatibleWithTemplateTemplate(
+ TemplateDecl *Template, TemplateTemplateParmDecl *Param,
+ const TemplateArgumentLoc &Arg) {
+ // C++0x [temp.arg.template]p1:
+ // A template-argument for a template template-parameter shall be
+ // the name of a class template or an alias template, expressed as an
+ // id-expression. When the template-argument names a class template, only
+ // primary class templates are considered when matching the
+ // template template argument with the corresponding parameter;
+ // partial specializations are not considered even if their
+ // parameter lists match that of the template template parameter.
+ //
+
+ TemplateNameKind Kind = TNK_Non_template;
+ unsigned DiagFoundKind = 0;
+
+ if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(Template)) {
+ switch (TTP->templateParameterKind()) {
+ case TemplateNameKind::TNK_Concept_template:
+ DiagFoundKind = 3;
+ break;
+ case TemplateNameKind::TNK_Var_template:
+ DiagFoundKind = 2;
+ break;
+ default:
+ DiagFoundKind = 1;
+ break;
+ }
+ Kind = TTP->templateParameterKind();
+ } else if (isa<ConceptDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Concept_template;
+ DiagFoundKind = 3;
+ } else if (isa<FunctionTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Function_template;
+ DiagFoundKind = 0;
+ } else if (isa<VarTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Var_template;
+ DiagFoundKind = 2;
+ } else if (isa<ClassTemplateDecl>(Template) ||
+ isa<TypeAliasTemplateDecl>(Template) ||
+ isa<BuiltinTemplateDecl>(Template)) {
+ Kind = TemplateNameKind::TNK_Type_template;
+ DiagFoundKind = 1;
+ } else {
+ assert(false && "Unexpected Decl");
+ }
+
+ if (Kind == Param->templateParameterKind()) {
+ return true;
+ }
+
+ unsigned DiagKind = 0;
+ switch (Param->templateParameterKind()) {
+ case TemplateNameKind::TNK_Concept_template:
+ DiagKind = 2;
+ break;
+ case TemplateNameKind::TNK_Var_template:
+ DiagKind = 1;
+ break;
+ default:
+ DiagKind = 0;
+ break;
+ }
+ Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template)
+ << DiagKind;
+ Diag(Template->getLocation(), diag::note_template_arg_refers_to_template_here)
+ << DiagFoundKind << Template;
+ return false;
+}
+
+/// Check a template argument against its corresponding
+/// template template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.template].
+/// It returns true if an error occurred, and false otherwise.
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
TemplateArgumentLoc &Arg,
@@ -7597,27 +7791,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
if (Template->isInvalidDecl())
return true;
- // C++0x [temp.arg.template]p1:
- // A template-argument for a template template-parameter shall be
- // the name of a class template or an alias template, expressed as an
- // id-expression. When the template-argument names a class template, only
- // primary class templates are considered when matching the
- // template template argument with the corresponding parameter;
- // partial specializations are not considered even if their
- // parameter lists match that of the template template parameter.
- //
- // Note that we also allow template template parameters here, which
- // will happen when we are dealing with, e.g., class template
- // partial specializations.
- if (!isa<ClassTemplateDecl>(Template) &&
- !isa<TemplateTemplateParmDecl>(Template) &&
- !isa<TypeAliasTemplateDecl>(Template) &&
- !isa<BuiltinTemplateDecl>(Template)) {
- assert(isa<FunctionTemplateDecl>(Template) &&
- "Only function templates are possible here");
- Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template);
- Diag(Template->getLocation(), diag::note_template_arg_refers_here_func)
- << Template;
+ if (!CheckDeclCompatibleWithTemplateTemplate(Template, Param, Arg)) {
+ return true;
}
// C++1z [temp.arg.template]p3: (DR 150)
@@ -7689,6 +7864,10 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) {
diag::note_template_param_external);
}
+/// Given a non-type template argument that refers to a
+/// declaration and the type of its corresponding non-type template
+/// parameter, produce an expression that properly refers to that
+/// declaration.
ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
NamedDecl *TemplateParam) {
@@ -8009,34 +8188,40 @@ static bool MatchTemplateParameterKind(
return false;
}
-
// For non-type template parameters, check the type of the parameter.
- if (NonTypeTemplateParmDecl *OldNTTP
- = dyn_cast<NonTypeTemplateParmDecl>(Old)) {
+ if (NonTypeTemplateParmDecl *OldNTTP =
+ dyn_cast<NonTypeTemplateParmDecl>(Old)) {
NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New);
- // C++20 [temp.over.link]p6:
- // Two [non-type] template-parameters are equivalent [if] they have
- // equivalent types ignoring the use of type-constraints for
- // placeholder types
- QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
- QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
- if (!S.Context.hasSameType(OldType, NewType)) {
- if (Complain) {
- unsigned NextDiag = diag::err_template_nontype_parm_different_type;
- if (TemplateArgLoc.isValid()) {
- S.Diag(TemplateArgLoc,
- diag::err_template_arg_template_params_mismatch);
- NextDiag = diag::note_template_nontype_parm_different_type;
+ // If we are matching a template template argument to a template
+ // template parameter and one of the non-type template parameter types
+ // is dependent, then we must wait until template instantiation time
+ // to actually compare the arguments.
+ if (Kind != Sema::TPL_TemplateTemplateParmMatch ||
+ (!OldNTTP->getType()->isDependentType() &&
+ !NewNTTP->getType()->isDependentType())) {
+ // C++20 [temp.over.link]p6:
+ // Two [non-type] template-parameters are equivalent [if] they have
+ // equivalent types ignoring the use of type-constraints for
+ // placeholder types
+ QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
+ QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
+ if (!S.Context.hasSameType(OldType, NewType)) {
+ if (Complain) {
+ unsigned NextDiag = diag::err_template_nontype_parm_different_type;
+ if (TemplateArgLoc.isValid()) {
+ S.Diag(TemplateArgLoc,
+ diag::err_template_arg_template_params_mismatch);
+ NextDiag = diag::note_template_nontype_parm_different_type;
+ }
+ S.Diag(NewNTTP->getLocation(), NextDiag)
+ << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
+ S.Diag(OldNTTP->getLocation(),
+ diag::note_template_nontype_parm_prev_declaration)
+ << OldNTTP->getType();
}
- S.Diag(NewNTTP->getLocation(), NextDiag)
- << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
- S.Diag(OldNTTP->getLocation(),
- diag::note_template_nontype_parm_prev_declaration)
- << OldNTTP->getType();
+ return false;
}
-
- return false;
}
}
// For template template parameters, check the template parameter types.
@@ -8045,6 +8230,8 @@ static bool MatchTemplateParameterKind(
else if (TemplateTemplateParmDecl *OldTTP =
dyn_cast<TemplateTemplateParmDecl>(Old)) {
TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
+ if (OldTTP->templateParameterKind() != NewTTP->templateParameterKind())
+ return false;
if (!S.TemplateParameterListsAreEqual(
NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
OldTTP->getTemplateParameters(), Complain,
@@ -8056,6 +8243,7 @@ static bool MatchTemplateParameterKind(
}
if (Kind != Sema::TPL_TemplateParamsEquivalent &&
+ Kind != Sema::TPL_TemplateTemplateParmMatch &&
!isa<TemplateTemplateParmDecl>(Old)) {
const Expr *NewC = nullptr, *OldC = nullptr;
@@ -8421,6 +8609,11 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
if (isa<NonTypeTemplateParmDecl>(DRE->getDecl()))
continue;
+ if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(ArgExpr);
+ ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+ continue;
+ }
+
// C++ [temp.class.spec]p9:
// Within the argument list of a class template partial
// specialization, the following restrictions apply:
@@ -9032,13 +9225,15 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl());
}
-bool Sema::CheckConceptUseInDefinition(ConceptDecl *Concept,
- SourceLocation Loc) {
- if (!Concept->isInvalidDecl() && !Concept->hasDefinition()) {
- Diag(Loc, diag::err_recursive_concept) << Concept;
- Diag(Concept->getLocation(), diag::note_declared_at);
+bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
+ if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
+ CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
+ Diag(Loc, diag::err_recursive_concept) << CE;
+ Diag(CE->getLocation(), diag::note_declared_at);
return true;
}
+ // Concept template parameters don't have a definition and can't
+ // be defined recursively.
return false;
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 9e56e697..0d70321 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -37,6 +37,7 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/Sema.h"
@@ -146,11 +147,7 @@ static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
bool OnlyDeduced, unsigned Level,
llvm::SmallBitVector &Deduced);
-/// If the given expression is of a form that permits the deduction
-/// of a non-type template parameter, return the declaration of that
-/// non-type template parameter.
-static const NonTypeTemplateParmDecl *
-getDeducedParameterFromExpr(const Expr *E, unsigned Depth) {
+static const Expr *unwrapExpressionForDeduction(const Expr *E) {
// If we are within an alias template, the expression may have undergone
// any number of parameter substitutions already.
while (true) {
@@ -170,18 +167,100 @@ getDeducedParameterFromExpr(const Expr *E, unsigned Depth) {
} else
break;
}
+ return E;
+}
+
+class NonTypeOrVarTemplateParmDecl {
+public:
+ NonTypeOrVarTemplateParmDecl(const NamedDecl *Template) : Template(Template) {
+ assert(
+ !Template || isa<NonTypeTemplateParmDecl>(Template) ||
+ (isa<TemplateTemplateParmDecl>(Template) &&
+ (cast<TemplateTemplateParmDecl>(Template)->templateParameterKind() ==
+ TNK_Var_template ||
+ cast<TemplateTemplateParmDecl>(Template)->templateParameterKind() ==
+ TNK_Concept_template)));
+ }
+
+ QualType getType() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return NTTP->getType();
+ return getTemplate()->templateParameterKind() == TNK_Concept_template
+ ? getTemplate()->getASTContext().BoolTy
+ : getTemplate()->getASTContext().DependentTy;
+ }
+
+ unsigned getDepth() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return NTTP->getDepth();
+ return getTemplate()->getDepth();
+ }
+
+ unsigned getIndex() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return NTTP->getIndex();
+ return getTemplate()->getIndex();
+ }
+
+ const TemplateTemplateParmDecl *getTemplate() const {
+ return cast<TemplateTemplateParmDecl>(Template);
+ }
+
+ const NonTypeTemplateParmDecl *getNTTP() const {
+ return cast<NonTypeTemplateParmDecl>(Template);
+ }
+
+ TemplateParameter asTemplateParam() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return const_cast<NonTypeTemplateParmDecl *>(NTTP);
+ return const_cast<TemplateTemplateParmDecl *>(getTemplate());
+ }
+
+ bool isExpandedParameterPack() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return NTTP->isExpandedParameterPack();
+ return getTemplate()->isExpandedParameterPack();
+ }
+
+ SourceLocation getLocation() const {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+ return NTTP->getLocation();
+ return getTemplate()->getLocation();
+ }
+
+ operator bool() const { return Template; }
+private:
+ const NamedDecl *Template;
+};
+
+/// If the given expression is of a form that permits the deduction
+/// of a non-type template parameter, return the declaration of that
+/// non-type template parameter.
+static NonTypeOrVarTemplateParmDecl
+getDeducedNTTParameterFromExpr(const Expr *E, unsigned Depth) {
+ // If we are within an alias template, the expression may have undergone
+ // any number of parameter substitutions already.
+ E = unwrapExpressionForDeduction(E);
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()))
if (NTTP->getDepth() == Depth)
return NTTP;
+ if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
+ ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+ if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+
+ if (TTP->getDepth() == Depth)
+ return TTP;
+ }
+ }
return nullptr;
}
-static const NonTypeTemplateParmDecl *
-getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
- return getDeducedParameterFromExpr(E, Info.getDeducedDepth());
+static const NonTypeOrVarTemplateParmDecl
+getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
+ return getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth());
}
/// Determine whether two declaration pointers refer to the same
@@ -386,29 +465,28 @@ checkDeducedTemplateArguments(ASTContext &Context,
/// deduction is funneled through here.
static TemplateDeductionResult
DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
- const NonTypeTemplateParmDecl *NTTP,
+ const NonTypeOrVarTemplateParmDecl NTTP,
const DeducedTemplateArgument &NewDeduced,
QualType ValueType, TemplateDeductionInfo &Info,
bool PartialOrdering,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool *HasDeducedAnyParam) {
- assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+ assert(NTTP.getDepth() == Info.getDeducedDepth() &&
"deducing non-type template argument with wrong depth");
DeducedTemplateArgument Result = checkDeducedTemplateArguments(
- S.Context, Deduced[NTTP->getIndex()], NewDeduced);
+ S.Context, Deduced[NTTP.getIndex()], NewDeduced);
if (Result.isNull()) {
- Info.Param = const_cast<NonTypeTemplateParmDecl*>(NTTP);
- Info.FirstArg = Deduced[NTTP->getIndex()];
+ Info.Param = NTTP.asTemplateParam();
+ Info.FirstArg = Deduced[NTTP.getIndex()];
Info.SecondArg = NewDeduced;
return TemplateDeductionResult::Inconsistent;
}
-
- Deduced[NTTP->getIndex()] = Result;
+ Deduced[NTTP.getIndex()] = Result;
if (!S.getLangOpts().CPlusPlus17)
return TemplateDeductionResult::Success;
- if (NTTP->isExpandedParameterPack())
+ if (NTTP.isExpandedParameterPack())
// FIXME: We may still need to deduce parts of the type here! But we
// don't have any way to find which slice of the type to use, and the
// type stored on the NTTP itself is nonsense. Perhaps the type of an
@@ -417,7 +495,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
// Get the type of the parameter for deduction. If it's a (dependent) array
// or function type, we will not have decayed it yet, so do that now.
- QualType ParamType = S.Context.getAdjustedParameterType(NTTP->getType());
+ QualType ParamType = S.Context.getAdjustedParameterType(NTTP.getType());
if (auto *Expansion = dyn_cast<PackExpansionType>(ParamType))
ParamType = Expansion->getPattern();
@@ -444,7 +522,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
/// from the given integral constant.
static TemplateDeductionResult DeduceNonTypeTemplateArgument(
Sema &S, TemplateParameterList *TemplateParams,
- const NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value,
+ NonTypeOrVarTemplateParmDecl NTTP, const llvm::APSInt &Value,
QualType ValueType, bool DeducedFromArrayBound, TemplateDeductionInfo &Info,
bool PartialOrdering, SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool *HasDeducedAnyParam) {
@@ -459,14 +537,14 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
/// from the given null pointer template argument type.
static TemplateDeductionResult
DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
- const NonTypeTemplateParmDecl *NTTP,
+ NonTypeOrVarTemplateParmDecl NTTP,
QualType NullPtrType, TemplateDeductionInfo &Info,
bool PartialOrdering,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool *HasDeducedAnyParam) {
Expr *Value = S.ImpCastExprToType(
new (S.Context) CXXNullPtrLiteralExpr(S.Context.NullPtrTy,
- NTTP->getLocation()),
+ NTTP.getLocation()),
NullPtrType,
NullPtrType->isMemberPointerType() ? CK_NullToMemberPointer
: CK_NullToPointer)
@@ -482,7 +560,7 @@ DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
/// \returns true if deduction succeeded, false otherwise.
static TemplateDeductionResult
DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
- const NonTypeTemplateParmDecl *NTTP, Expr *Value,
+ NonTypeOrVarTemplateParmDecl NTTP, Expr *Value,
TemplateDeductionInfo &Info, bool PartialOrdering,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
bool *HasDeducedAnyParam) {
@@ -497,7 +575,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
/// \returns true if deduction succeeded, false otherwise.
static TemplateDeductionResult
DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
- const NonTypeTemplateParmDecl *NTTP, ValueDecl *D,
+ NonTypeOrVarTemplateParmDecl NTTP, ValueDecl *D,
QualType T, TemplateDeductionInfo &Info,
bool PartialOrdering,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
@@ -1930,14 +2008,14 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Determine the array bound is something we can deduce.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, DAP->getSizeExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, DAP->getSizeExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
// We can perform template argument deduction for the given non-type
// template parameter.
- assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+ assert(NTTP.getDepth() == Info.getDeducedDepth() &&
"saw non-type template parameter with wrong depth");
if (const auto *CAA = dyn_cast<ConstantArrayType>(AA)) {
llvm::APSInt Size(CAA->getSize());
@@ -1994,10 +2072,10 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
// deducing through the noexcept-specifier if it's part of the canonical
// type. libstdc++ relies on this.
Expr *NoexceptExpr = FPP->getNoexceptExpr();
- if (const NonTypeTemplateParmDecl *NTTP =
- NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr)
+ if (NonTypeOrVarTemplateParmDecl NTTP =
+ NoexceptExpr ? getDeducedNTTParameterFromExpr(Info, NoexceptExpr)
: nullptr) {
- assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+ assert(NTTP.getDepth() == Info.getDeducedDepth() &&
"saw non-type template parameter with wrong depth");
llvm::APSInt Noexcept(1);
@@ -2191,8 +2269,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the vector size, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2217,8 +2295,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the vector size, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2246,8 +2324,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the vector size, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2271,8 +2349,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the vector size, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2348,8 +2426,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return TemplateDeductionResult::NonDeducedMismatch;
}
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, ParamExpr);
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, ParamExpr);
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2395,8 +2473,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the address space, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, ASP->getAddrSpaceExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2420,8 +2498,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
return Result;
// Perform deduction on the address space, if we can.
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, ASP->getAddrSpaceExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2440,8 +2518,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
if (IP->isUnsigned() != IA->isUnsigned())
return TemplateDeductionResult::NonDeducedMismatch;
- const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, IP->getNumBitsExpr());
+ NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, IP->getNumBitsExpr());
if (!NTTP)
return TemplateDeductionResult::Success;
@@ -2573,8 +2651,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::NonDeducedMismatch;
case TemplateArgument::Expression:
- if (const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, P.getAsExpr())) {
+ if (NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(Info, P.getAsExpr())) {
switch (A.getKind()) {
case TemplateArgument::Expression: {
const Expr *E = A.getAsExpr();
@@ -2628,7 +2706,6 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
}
llvm_unreachable("Unknown template argument kind");
}
-
// Can't deduce anything, but that's okay.
return TemplateDeductionResult::Success;
case TemplateArgument::Pack:
@@ -4392,8 +4469,8 @@ static TemplateDeductionResult DeduceFromInitializerList(
// from the length of the initializer list.
if (auto *DependentArrTy = dyn_cast_or_null<DependentSizedArrayType>(ArrTy)) {
// Determine the array bound is something we can deduce.
- if (const NonTypeTemplateParmDecl *NTTP =
- getDeducedParameterFromExpr(Info, DependentArrTy->getSizeExpr())) {
+ if (NonTypeOrVarTemplateParmDecl NTTP = getDeducedNTTParameterFromExpr(
+ Info, DependentArrTy->getSizeExpr())) {
// We can perform template argument deduction for the given non-type
// template parameter.
// C++ [temp.deduct.type]p13:
@@ -5098,7 +5175,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
AutoTypeLoc TypeLoc,
QualType Deduced) {
ConstraintSatisfaction Satisfaction;
- ConceptDecl *Concept = Type.getTypeConstraintConcept();
+ ConceptDecl *Concept = cast<ConceptDecl>(Type.getTypeConstraintConcept());
TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(),
TypeLoc.getRAngleLoc());
TemplateArgs.addArgument(
@@ -6654,6 +6731,18 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
Used[NTTP->getIndex()] = true;
return true;
}
+
+ bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) override {
+ if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
+ if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+ if (TTP->getDepth() == Depth)
+ Used[TTP->getIndex()] = true;
+ }
+ for (auto &TLoc : ULE->template_arguments())
+ DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
+ }
+ return true;
+ }
};
}
@@ -6675,17 +6764,28 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E))
E = Expansion->getPattern();
- const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(E, Depth);
- if (!NTTP)
+ E = unwrapExpressionForDeduction(E);
+ if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
+ ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+ if (const auto *TTP = ULE->getTemplateTemplateDecl())
+ Used[TTP->getIndex()] = true;
+ for (auto &TLoc : ULE->template_arguments())
+ MarkUsedTemplateParameters(Ctx, TLoc.getArgument(), OnlyDeduced, Depth,
+ Used);
return;
+ }
- if (NTTP->getDepth() == Depth)
- Used[NTTP->getIndex()] = true;
+ const NonTypeOrVarTemplateParmDecl NTTP =
+ getDeducedNTTParameterFromExpr(E, Depth);
+ if (!NTTP)
+ return;
+ if (NTTP.getDepth() == Depth)
+ Used[NTTP.getIndex()] = true;
// In C++17 mode, additional arguments may be deduced from the type of a
// non-type argument.
if (Ctx.getLangOpts().CPlusPlus17)
- MarkUsedTemplateParameters(Ctx, NTTP->getType(), OnlyDeduced, Depth, Used);
+ MarkUsedTemplateParameters(Ctx, NTTP.getType(), OnlyDeduced, Depth, Used);
}
/// Mark the template parameters that are used by the given
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index d84d0ca1..0d96d18 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1899,8 +1899,7 @@ namespace {
private:
ExprResult
- transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
- const NonTypeTemplateParmDecl *parm,
+ transformNonTypeTemplateParmRef(Decl *AssociatedDecl, const NamedDecl *parm,
SourceLocation loc, TemplateArgument arg,
UnsignedOrNone PackIndex, bool Final);
};
@@ -2338,22 +2337,25 @@ TemplateInstantiator::TransformOpenACCRoutineDeclAttr(
}
ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
- Decl *AssociatedDecl, const NonTypeTemplateParmDecl *parm,
- SourceLocation loc, TemplateArgument arg, UnsignedOrNone PackIndex,
- bool Final) {
+ Decl *AssociatedDecl, const NamedDecl *parm, SourceLocation loc,
+ TemplateArgument arg, UnsignedOrNone PackIndex, bool Final) {
ExprResult result;
// Determine the substituted parameter type. We can usually infer this from
// the template argument, but not always.
auto SubstParamType = [&] {
- QualType T;
- if (parm->isExpandedParameterPack())
- T = parm->getExpansionType(*SemaRef.ArgPackSubstIndex);
- else
- T = parm->getType();
- if (parm->isParameterPack() && isa<PackExpansionType>(T))
- T = cast<PackExpansionType>(T)->getPattern();
- return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(parm)) {
+ QualType T;
+ if (NTTP->isExpandedParameterPack())
+ T = NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex);
+ else
+ T = NTTP->getType();
+ if (parm->isParameterPack() && isa<PackExpansionType>(T))
+ T = cast<PackExpansionType>(T)->getPattern();
+ return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
+ }
+ return SemaRef.SubstType(arg.getAsExpr()->getType(), TemplateArgs, loc,
+ parm->getDeclName());
};
bool refParam = false;
@@ -2408,7 +2410,9 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
Expr *resultExpr = result.get();
return new (SemaRef.Context) SubstNonTypeTemplateParmExpr(
resultExpr->getType(), resultExpr->getValueKind(), loc, resultExpr,
- AssociatedDecl, parm->getIndex(), PackIndex, refParam, Final);
+ AssociatedDecl,
+ clang::getDepthAndIndex(const_cast<NamedDecl *>(parm)).second, PackIndex,
+ refParam, Final);
}
ExprResult
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 233bb65..87ec4f7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3784,14 +3784,14 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
Param = TemplateTemplateParmDecl::Create(
SemaRef.Context, Owner, D->getLocation(),
D->getDepth() - TemplateArgs.getNumSubstitutedLevels(),
- D->getPosition(), D->getIdentifier(), D->wasDeclaredWithTypename(),
- InstParams, ExpandedParams);
+ D->getPosition(), D->getIdentifier(), D->templateParameterKind(),
+ D->wasDeclaredWithTypename(), InstParams, ExpandedParams);
else
Param = TemplateTemplateParmDecl::Create(
SemaRef.Context, Owner, D->getLocation(),
D->getDepth() - TemplateArgs.getNumSubstitutedLevels(),
D->getPosition(), D->isParameterPack(), D->getIdentifier(),
- D->wasDeclaredWithTypename(), InstParams);
+ D->templateParameterKind(), D->wasDeclaredWithTypename(), InstParams);
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
NestedNameSpecifierLoc QualifierLoc =
D->getDefaultArgument().getTemplateQualifierLoc();
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index b0a673d..d2baa2e 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -310,6 +310,16 @@ class CollectUnexpandedParameterPacksVisitor
return DynamicRecursiveASTVisitor::TraverseLambdaCapture(Lambda, C, Init);
}
+ bool TraverseUnresolvedLookupExpr(UnresolvedLookupExpr *E) override {
+ if (E->getNumDecls() == 1) {
+ NamedDecl *ND = *E->decls_begin();
+ if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND);
+ TTP && TTP->isParameterPack())
+ addUnexpanded(ND, E->getBeginLoc());
+ }
+ return DynamicRecursiveASTVisitor::TraverseUnresolvedLookupExpr(E);
+ }
+
#ifndef NDEBUG
bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) override {
ContainsIntermediatePacks = true;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 7dbd4bb..1289bed 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1306,12 +1306,12 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
? AutoTypeKeyword::DecltypeAuto
: AutoTypeKeyword::Auto;
- ConceptDecl *TypeConstraintConcept = nullptr;
+ TemplateDecl *TypeConstraintConcept = nullptr;
llvm::SmallVector<TemplateArgument, 8> TemplateArgs;
if (DS.isConstrainedAuto()) {
if (TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId()) {
TypeConstraintConcept =
- cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl());
+ cast<TemplateDecl>(TemplateId->Template.get().getAsTemplateDecl());
TemplateArgumentListInfo TemplateArgsInfo;
TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc);
TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc);
@@ -3090,15 +3090,13 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
if (!Invalid) {
UsingShadowDecl *USD =
TemplateId->Template.get().getAsUsingShadowDecl();
- auto *CD =
- cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl());
+ TemplateDecl *CD = TemplateId->Template.get().getAsTemplateDecl();
S.AttachTypeConstraint(
D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
DeclarationNameInfo(DeclarationName(TemplateId->Name),
TemplateId->TemplateNameLoc),
CD,
- /*FoundDecl=*/
- USD ? cast<NamedDecl>(USD) : CD,
+ /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c7428d1..5ff5fbf5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -610,6 +610,10 @@ public:
TemplateArgumentLoc &Output,
bool Uneval = false);
+ TemplateArgument
+ TransformNamedTemplateTemplateArgument(CXXScopeSpec &SS, TemplateName Name,
+ SourceLocation NameLoc);
+
/// Transform the given set of template arguments.
///
/// By default, this operation transforms all of the template arguments
@@ -4843,6 +4847,15 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
llvm_unreachable("overloaded function decl survived to here");
}
+template <typename Derived>
+TemplateArgument TreeTransform<Derived>::TransformNamedTemplateTemplateArgument(
+ CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc) {
+ TemplateName TN = getDerived().TransformTemplateName(SS, Name, NameLoc);
+ if (TN.isNull())
+ return TemplateArgument();
+ return TemplateArgument(TN);
+}
+
template<typename Derived>
void TreeTransform<Derived>::InventTemplateArgumentLoc(
const TemplateArgument &Arg,
@@ -4927,13 +4940,13 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
- TemplateName Template = getDerived().TransformTemplateName(
+
+ TemplateArgument Out = getDerived().TransformNamedTemplateTemplateArgument(
SS, Arg.getAsTemplate(), Input.getTemplateNameLoc());
- if (Template.isNull())
+ if (Out.isNull())
return true;
-
- Output = TemplateArgumentLoc(SemaRef.Context, TemplateArgument(Template),
- QualifierLoc, Input.getTemplateNameLoc());
+ Output = TemplateArgumentLoc(SemaRef.Context, Out, QualifierLoc,
+ Input.getTemplateNameLoc());
return false;
}
@@ -5029,10 +5042,8 @@ template<typename InputIterator>
bool TreeTransform<Derived>::TransformTemplateArguments(
InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
bool Uneval) {
- for (; First != Last; ++First) {
+ for (TemplateArgumentLoc In : llvm::make_range(First, Last)) {
TemplateArgumentLoc Out;
- TemplateArgumentLoc In = *First;
-
if (In.getArgument().getKind() == TemplateArgument::Pack) {
// Unpack argument packs, which we translate them into separate
// arguments.
@@ -5042,11 +5053,10 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
- if (TransformTemplateArguments(PackLocIterator(*this,
- In.getArgument().pack_begin()),
- PackLocIterator(*this,
- In.getArgument().pack_end()),
- Outputs, Uneval))
+ if (TransformTemplateArguments(
+ PackLocIterator(*this, In.getArgument().pack_begin()),
+ PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+ Uneval))
return true;
continue;
@@ -11730,20 +11740,26 @@ class OpenACCClauseTransform final
SemaOpenACC::OpenACCParsedClause &ParsedClause;
OpenACCClause *NewClause = nullptr;
+ ExprResult VisitVar(Expr *VarRef) {
+ ExprResult Res = Self.TransformExpr(VarRef);
+
+ if (!Res.isUsable())
+ return Res;
+
+ Res = Self.getSema().OpenACC().ActOnVar(ParsedClause.getDirectiveKind(),
+ ParsedClause.getClauseKind(),
+ Res.get());
+
+ return Res;
+ }
+
llvm::SmallVector<Expr *> VisitVarList(ArrayRef<Expr *> VarList) {
llvm::SmallVector<Expr *> InstantiatedVarList;
for (Expr *CurVar : VarList) {
- ExprResult Res = Self.TransformExpr(CurVar);
+ ExprResult VarRef = VisitVar(CurVar);
- if (!Res.isUsable())
- continue;
-
- Res = Self.getSema().OpenACC().ActOnVar(ParsedClause.getDirectiveKind(),
- ParsedClause.getClauseKind(),
- Res.get());
-
- if (Res.isUsable())
- InstantiatedVarList.push_back(Res.get());
+ if (VarRef.isUsable())
+ InstantiatedVarList.push_back(VarRef.get());
}
return InstantiatedVarList;
@@ -11870,12 +11886,31 @@ void OpenACCClauseTransform<Derived>::VisitNumGangsClause(
template <typename Derived>
void OpenACCClauseTransform<Derived>::VisitPrivateClause(
const OpenACCPrivateClause &C) {
- ParsedClause.setVarListDetails(VisitVarList(C.getVarList()),
+ llvm::SmallVector<Expr *> InstantiatedVarList;
+ llvm::SmallVector<VarDecl *> InitRecipes;
+
+ for (const auto [RefExpr, InitRecipe] :
+ llvm::zip(C.getVarList(), C.getInitRecipes())) {
+ ExprResult VarRef = VisitVar(RefExpr);
+
+ if (VarRef.isUsable()) {
+ InstantiatedVarList.push_back(VarRef.get());
+
+ // We only have to create a new one if it is dependent, and Sema won't
+ // make one of these unless the type is non-dependent.
+ if (InitRecipe)
+ InitRecipes.push_back(InitRecipe);
+ else
+ InitRecipes.push_back(
+ Self.getSema().OpenACC().CreateInitRecipe(VarRef.get()));
+ }
+ }
+ ParsedClause.setVarListDetails(InstantiatedVarList,
OpenACCModifierKind::Invalid);
NewClause = OpenACCPrivateClause::Create(
Self.getSema().getASTContext(), ParsedClause.getBeginLoc(),
- ParsedClause.getLParenLoc(), ParsedClause.getVarList(),
+ ParsedClause.getLParenLoc(), ParsedClause.getVarList(), InitRecipes,
ParsedClause.getEndLoc());
}
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 682d263..95e92cf 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8317,6 +8317,9 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) {
NewLoaded = Context.getCFConstantStringTagDecl();
break;
+ case PREDEF_DECL_BUILTIN_MS_TYPE_INFO_TAG_ID:
+ return Context.getMSTypeInfoTagDecl();
+
#define BuiltinTemplate(BTName) \
case PREDEF_DECL##BTName##_ID: \
if (Context.Decl##BTName) \
@@ -12851,8 +12854,13 @@ OpenACCClause *ASTRecordReader::readOpenACCClause() {
case OpenACCClauseKind::Private: {
SourceLocation LParenLoc = readSourceLocation();
llvm::SmallVector<Expr *> VarList = readOpenACCVarList();
+
+ llvm::SmallVector<VarDecl *> RecipeList;
+ for (unsigned I = 0; I < VarList.size(); ++I)
+ RecipeList.push_back(readDeclAs<VarDecl>());
+
return OpenACCPrivateClause::Create(getContext(), BeginLoc, LParenLoc,
- VarList, EndLoc);
+ VarList, RecipeList, EndLoc);
}
case OpenACCClauseKind::Host: {
SourceLocation LParenLoc = readSourceLocation();
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index cdaf38d..2c7beb4 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2742,6 +2742,7 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) {
void ASTDeclReader::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
VisitTemplateDecl(D);
+ D->ParameterKind = static_cast<TemplateNameKind>(Record.readInt());
D->setDeclaredWithTypename(Record.readBool());
// TemplateParmPosition.
D->setDepth(Record.readInt());
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 0166e49..3f37dfb 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2086,13 +2086,12 @@ void ASTStmtReader::VisitOverloadExpr(OverloadExpr *E) {
assert((E->hasTemplateKWAndArgsInfo() == HasTemplateKWAndArgsInfo) &&
"Wrong HasTemplateKWAndArgsInfo!");
+ unsigned NumTemplateArgs = 0;
if (HasTemplateKWAndArgsInfo) {
- unsigned NumTemplateArgs = Record.readInt();
+ NumTemplateArgs = Record.readInt();
ReadTemplateKWAndArgsInfo(*E->getTrailingASTTemplateKWAndArgsInfo(),
E->getTrailingTemplateArgumentLoc(),
NumTemplateArgs);
- assert((E->getNumTemplateArgs() == NumTemplateArgs) &&
- "Wrong NumTemplateArgs!");
}
UnresolvedSet<8> Decls;
@@ -2108,6 +2107,9 @@ void ASTStmtReader::VisitOverloadExpr(OverloadExpr *E) {
Results[I] = (Iter + I).getPair();
}
+ assert((E->getNumTemplateArgs() == NumTemplateArgs) &&
+ "Wrong NumTemplateArgs!");
+
E->NameInfo = Record.readDeclarationNameInfo();
E->QualifierLoc = Record.readNestedNameSpecifierLoc();
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index a6957e5..955f7b9 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5618,6 +5618,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
PREDEF_DECL_BUILTIN_MS_VA_LIST_ID);
RegisterPredefDecl(Context.MSGuidTagDecl,
PREDEF_DECL_BUILTIN_MS_GUID_ID);
+ RegisterPredefDecl(Context.MSTypeInfoTagDecl,
+ PREDEF_DECL_BUILTIN_MS_TYPE_INFO_TAG_ID);
RegisterPredefDecl(Context.ExternCContext, PREDEF_DECL_EXTERN_C_CONTEXT_ID);
RegisterPredefDecl(Context.CFConstantStringTypeDecl,
PREDEF_DECL_CF_CONSTANT_STRING_ID);
@@ -8738,6 +8740,9 @@ void ASTRecordWriter::writeOpenACCClause(const OpenACCClause *C) {
const auto *PC = cast<OpenACCPrivateClause>(C);
writeSourceLocation(PC->getLParenLoc());
writeOpenACCVarList(PC);
+
+ for (VarDecl *VD : PC->getInitRecipes())
+ AddDeclRef(VD);
return;
}
case OpenACCClauseKind::Host: {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index e414910..6fd25b9 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2149,6 +2149,7 @@ void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
Record.push_back(D->getNumExpansionTemplateParameters());
VisitTemplateDecl(D);
+ Record.push_back(D->templateParameterKind());
Record.push_back(D->wasDeclaredWithTypename());
// TemplateParmPosition.
Record.push_back(D->getDepth());
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 22dd3f0..7836041 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -104,6 +104,7 @@ add_clang_library(clangStaticAnalyzerCheckers
SmartPtrChecker.cpp
SmartPtrModeling.cpp
StackAddrEscapeChecker.cpp
+ StoreToImmutableChecker.cpp
StdLibraryFunctionsChecker.cpp
StdVariantChecker.cpp
STLAlgorithmModeling.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp
new file mode 100644
index 0000000..afad419
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/StoreToImmutableChecker.cpp
@@ -0,0 +1,188 @@
+//=== StoreToImmutableChecker.cpp - Store to immutable memory ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines StoreToImmutableChecker, a checker that detects writes
+// to immutable memory regions. This implements part of SEI CERT Rule ENV30-C.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class StoreToImmutableChecker : public Checker<check::Bind> {
+ const BugType BT{this, "Write to immutable memory", "CERT Environment (ENV)"};
+
+public:
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+static bool isInitializationContext(const Stmt *S, CheckerContext &C) {
+ // Check if this is a DeclStmt (variable declaration)
+ if (isa<DeclStmt>(S))
+ return true;
+
+ // This part is specific for initialization of const lambdas pre-C++17.
+ // Lets look at the AST of the statement:
+ // ```
+ // const auto lambda = [](){};
+ // ```
+ //
+ // The relevant part of the AST for this case prior to C++17 is:
+ // ...
+ // `-DeclStmt
+ // `-VarDecl
+ // `-ExprWithCleanups
+ // `-CXXConstructExpr
+ // ...
+ // In C++17 and later, the AST is different:
+ // ...
+ // `-DeclStmt
+ // `-VarDecl
+ // `-ImplicitCastExpr
+ // `-LambdaExpr
+ // |-CXXRecordDecl
+ // `-CXXConstructExpr
+ // ...
+ // And even beside this, the statement `S` that is given to the checkBind
+ // callback is the VarDecl in C++17 and later, and the CXXConstructExpr in
+ // C++14 and before. So in order to support the C++14 we need the following
+ // ugly hack to detect whether this construction is used to initialize a
+ // variable.
+ //
+ // FIXME: This should be eliminated by improving the API of checkBind to
+ // ensure that it consistently passes the `VarDecl` (instead of the
+ // `CXXConstructExpr`) when the constructor call denotes the initialization
+ // of a variable with a lambda, or maybe less preferably, try the more
+ // invasive approach of passing the information forward to the checkers
+ // whether the current bind is an initialization or an assignment.
+ const auto *ConstructExp = dyn_cast<CXXConstructExpr>(S);
+ return ConstructExp && ConstructExp->isElidable();
+}
+
+static bool isEffectivelyConstRegion(const MemRegion *MR, CheckerContext &C) {
+ if (isa<GlobalImmutableSpaceRegion>(MR))
+ return true;
+
+ // Check if this is a TypedRegion with a const-qualified type
+ if (const auto *TR = dyn_cast<TypedRegion>(MR)) {
+ QualType LocationType = TR->getDesugaredLocationType(C.getASTContext());
+ if (LocationType->isPointerOrReferenceType())
+ LocationType = LocationType->getPointeeType();
+ if (LocationType.isConstQualified())
+ return true;
+ }
+
+ // Check if this is a SymbolicRegion with a const-qualified pointee type
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
+ QualType PointeeType = SR->getPointeeStaticType();
+ if (PointeeType.isConstQualified())
+ return true;
+ }
+
+ // NOTE: The above branches do not cover AllocaRegion. We do not need to check
+ // AllocaRegion, as it models untyped memory, that is allocated on the stack.
+
+ return false;
+}
+
+static const MemRegion *getInnermostConstRegion(const MemRegion *MR,
+ CheckerContext &C) {
+ while (true) {
+ if (isEffectivelyConstRegion(MR, C))
+ return MR;
+ if (auto *SR = dyn_cast<SubRegion>(MR))
+ MR = SR->getSuperRegion();
+ else
+ return nullptr;
+ }
+}
+
+static const DeclRegion *
+getInnermostEnclosingConstDeclRegion(const MemRegion *MR, CheckerContext &C) {
+ while (true) {
+ if (const auto *DR = dyn_cast<DeclRegion>(MR)) {
+ const ValueDecl *D = DR->getDecl();
+ QualType DeclaredType = D->getType();
+ if (DeclaredType.isConstQualified())
+ return DR;
+ }
+ if (auto *SR = dyn_cast<SubRegion>(MR))
+ MR = SR->getSuperRegion();
+ else
+ return nullptr;
+ }
+}
+
+void StoreToImmutableChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
+ CheckerContext &C) const {
+ // We are only interested in stores to memory regions
+ const MemRegion *MR = Loc.getAsRegion();
+ if (!MR)
+ return;
+
+ // Skip variable declarations and initializations - we only want to catch
+ // actual writes
+ // FIXME: If the API of checkBind would allow to distinguish between
+ // initialization and assignment, we could use that instead.
+ if (isInitializationContext(S, C))
+ return;
+
+ // Check if the region is in the global immutable space
+ const MemSpaceRegion *MS = MR->getMemorySpace(C.getState());
+ const bool IsGlobalImmutableSpace = isa<GlobalImmutableSpaceRegion>(MS);
+ // Check if the region corresponds to a const variable
+ const MemRegion *InnermostConstRegion = getInnermostConstRegion(MR, C);
+ if (!IsGlobalImmutableSpace && !InnermostConstRegion)
+ return;
+
+ SmallString<64> WarningMessage{"Trying to write to immutable memory"};
+ if (IsGlobalImmutableSpace)
+ WarningMessage += " in global read-only storage";
+
+ // Generate the bug report
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, WarningMessage, N);
+ R->addRange(S->getSourceRange());
+
+ // Generate a note if the location that is being written to has a
+ // declaration or if it is a subregion of a const region with a declaration.
+ const DeclRegion *DR =
+ getInnermostEnclosingConstDeclRegion(InnermostConstRegion, C);
+ if (DR) {
+ const char *NoteMessage =
+ (DR != MR) ? "Enclosing memory region is declared as immutable here"
+ : "Memory region is declared as immutable here";
+ R->addNote(NoteMessage, PathDiagnosticLocation::create(
+ DR->getDecl(), C.getSourceManager()));
+ }
+
+ // For this checker, we are only interested in the value being written, no
+ // need to mark the value being assigned interesting.
+
+ C.emitReport(std::move(R));
+}
+
+void ento::registerStoreToImmutableChecker(CheckerManager &mgr) {
+ mgr.registerChecker<StoreToImmutableChecker>();
+}
+
+bool ento::shouldRegisterStoreToImmutableChecker(const CheckerManager &mgr) {
+ return true;
+}