18 changes: 9 additions & 9 deletions clang/include/clang/Basic/riscv_vector.td
Original file line number Diff line number Diff line change
Expand Up @@ -2688,7 +2688,7 @@ multiclass RVVSignedWidenBinBuiltinSetVwsll

let UnMaskedPolicyScheme = HasPassthruOperand in {
// zvkb
let RequiredFeatures = ["Zvkb", "Experimental"] in {
let RequiredFeatures = ["Zvkb"] in {
defm vandn : RVVUnsignedBinBuiltinSet;
defm vbrev8 : RVVOutBuiltinSetZvbb;
defm vrev8 : RVVOutBuiltinSetZvbb;
Expand All @@ -2697,7 +2697,7 @@ let UnMaskedPolicyScheme = HasPassthruOperand in {
}

// zvbb
let RequiredFeatures = ["Zvbb", "Experimental"] in {
let RequiredFeatures = ["Zvbb"] in {
defm vbrev : RVVOutBuiltinSetZvbb;
defm vclz : RVVOutBuiltinSetZvbb;
defm vctz : RVVOutBuiltinSetZvbb;
Expand All @@ -2708,21 +2708,21 @@ let UnMaskedPolicyScheme = HasPassthruOperand in {
}

// zvbc
let RequiredFeatures = ["Zvbc", "Experimental"] in {
let RequiredFeatures = ["Zvbc"] in {
defm vclmul : RVVInt64BinBuiltinSet;
defm vclmulh : RVVInt64BinBuiltinSet;
}
}

let UnMaskedPolicyScheme = HasPolicyOperand, HasMasked = false in {
// zvkg
let RequiredFeatures = ["Zvkg", "Experimental"] in {
let RequiredFeatures = ["Zvkg"] in {
defm vghsh : RVVOutOp2BuiltinSetVVZvk;
defm vgmul : RVVOutBuiltinSetZvk<HasVV=1, HasVS=0>;
}

// zvkned
let RequiredFeatures = ["Zvkned", "Experimental"] in {
let RequiredFeatures = ["Zvkned"] in {
defm vaesdf : RVVOutBuiltinSetZvk;
defm vaesdm : RVVOutBuiltinSetZvk;
defm vaesef : RVVOutBuiltinSetZvk;
Expand All @@ -2734,28 +2734,28 @@ let UnMaskedPolicyScheme = HasPolicyOperand, HasMasked = false in {
}

// zvknha
let RequiredFeatures = ["Zvknha", "Experimental"] in {
let RequiredFeatures = ["Zvknha"] in {
defm vsha2ch : RVVOutOp2BuiltinSetVVZvk<"i">;
defm vsha2cl : RVVOutOp2BuiltinSetVVZvk<"i">;
defm vsha2ms : RVVOutOp2BuiltinSetVVZvk<"i">;
}

// zvknhb
let RequiredFeatures = ["Zvknhb", "Experimental"] in {
let RequiredFeatures = ["Zvknhb"] in {
defm vsha2ch : RVVOutOp2BuiltinSetVVZvk<"il">;
defm vsha2cl : RVVOutOp2BuiltinSetVVZvk<"il">;
defm vsha2ms : RVVOutOp2BuiltinSetVVZvk<"il">;
}

// zvksed
let RequiredFeatures = ["Zvksed", "Experimental"] in {
let RequiredFeatures = ["Zvksed"] in {
let UnMaskedPolicyScheme = HasPassthruOperand in
defm vsm4k : RVVOutOp1BuiltinSet<"vsm4k", "i", [["vi", "Uv", "UvUvKz"]]>;
defm vsm4r : RVVOutBuiltinSetZvk;
}

// zvksh
let RequiredFeatures = ["Zvksh", "Experimental"] in {
let RequiredFeatures = ["Zvksh"] in {
defm vsm3c : RVVOutOp2BuiltinSetVIZvk;
let UnMaskedPolicyScheme = HasPassthruOperand in
defm vsm3me : RVVOutOp1BuiltinSet<"vsm3me", "i", [["vv", "Uv", "UvUvUv"]]>;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/CodeGen/CodeGenAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class CodeGenAction : public ASTFrontendAction {
bool loadLinkModules(CompilerInstance &CI);

protected:
bool BeginSourceFileAction(CompilerInstance &CI) override;
bool BeginInvocation(CompilerInstance &CI) override;

/// Create a new code generation action. If the optional \p _VMContext
/// parameter is supplied, the action uses it without taking ownership,
Expand Down
11 changes: 5 additions & 6 deletions clang/include/clang/ExtractAPI/API.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "clang/AST/RawCommentList.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/TargetParser/Triple.h"
Expand Down Expand Up @@ -1420,9 +1420,8 @@ class APISet {
typename std::enable_if_t<std::is_base_of_v<APIRecord, RecordTy>, RecordTy> *
createRecord(StringRef USR, StringRef Name, CtorArgsContTy &&...CtorArgs);

auto getTopLevelRecords() const {
return llvm::iterator_range<decltype(TopLevelRecords)::iterator>(
TopLevelRecords);
ArrayRef<const APIRecord *> getTopLevelRecords() const {
return TopLevelRecords;
}

void removeRecord(StringRef USR);
Expand Down Expand Up @@ -1455,7 +1454,7 @@ class APISet {
// lives in the BumpPtrAllocator.
using APIRecordStoredPtr = std::unique_ptr<APIRecord, APIRecordDeleter>;
llvm::DenseMap<StringRef, APIRecordStoredPtr> USRBasedLookupTable;
llvm::SmallPtrSet<const APIRecord *, 32> TopLevelRecords;
llvm::SmallVector<const APIRecord *, 32> TopLevelRecords;

public:
const std::string ProductName;
Expand All @@ -1481,7 +1480,7 @@ APISet::createRecord(StringRef USR, StringRef Name,
dyn_cast_if_present<RecordContext>(Record->Parent.Record))
ParentContext->addToRecordChain(Record);
else
TopLevelRecords.insert(Record);
TopLevelRecords.push_back(Record);
} else {
Record = dyn_cast<RecordTy>(Result.first->second.get());
}
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Frontend/FrontendActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,13 @@ class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
};

bool BeginInvocationForModules(CompilerInstance &CI);

/// Generates full BMI (which contains full information to generate the object
/// files) for C++20 Named Modules.
class GenerateModuleInterfaceAction : public GenerateModuleAction {
protected:
bool BeginSourceFileAction(CompilerInstance &CI) override;
bool BeginInvocation(CompilerInstance &CI) override;

std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/ExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class ExternalSemaSource : public ExternalASTSource {
/// Notify the external source that a lambda was assigned a mangling number.
/// This enables the external source to track the correspondence between
/// lambdas and mangling numbers if necessary.
virtual void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) {}
virtual void AssignedLambdaNumbering(CXXRecordDecl *Lambda) {}

/// LLVM-style RTTI.
/// \{
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/MultiplexExternalSemaSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
QualType T) override;

// Inform all attached sources that a mangling number was assigned.
void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override;
void AssignedLambdaNumbering(CXXRecordDecl *Lambda) override;

/// LLVM-style RTTI.
/// \{
Expand Down
10 changes: 9 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,9 @@ class Sema final : public SemaBase {
/// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types.
void inferGslOwnerPointerAttribute(CXXRecordDecl *Record);

/// Add [[clang:::lifetimebound]] attr for std:: functions and methods.
void inferLifetimeBoundAttribute(FunctionDecl *FD);

/// Add [[gsl::Pointer]] attributes for std:: types.
void inferGslPointerAttribute(TypedefNameDecl *TD);

Expand Down Expand Up @@ -13280,6 +13283,10 @@ class Sema final : public SemaBase {
/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is
/// acceptable as the top level type of the result.
///
/// \param IsIncompleteSubstitution If provided, the pointee will be set
/// whenever substitution would perform a replacement with a null or
/// non-existent template argument.
///
/// \returns If the instantiation succeeds, the instantiated
/// type. Otherwise, produces diagnostics and returns a NULL type.
TypeSourceInfo *SubstType(TypeSourceInfo *T,
Expand All @@ -13289,7 +13296,8 @@ class Sema final : public SemaBase {

QualType SubstType(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity);
SourceLocation Loc, DeclarationName Entity,
bool *IsIncompleteSubstitution = nullptr);

TypeSourceInfo *SubstType(TypeLoc TL,
const MultiLevelTemplateArgumentList &TemplateArgs,
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -2149,7 +2149,7 @@ class ASTReader
llvm::MapVector<const FunctionDecl *, std::unique_ptr<LateParsedTemplate>>
&LPTMap) override;

void AssignedLambdaNumbering(const CXXRecordDecl *Lambda) override;
void AssignedLambdaNumbering(CXXRecordDecl *Lambda) override;

/// Load a selector from disk, registering its ID if it exists.
void LoadSelector(Selector Sel);
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@ class ASTWriter : public ASTDeserializationListener,
std::vector<SourceRange> NonAffectingRanges;
std::vector<SourceLocation::UIntTy> NonAffectingOffsetAdjustments;

/// A list of classes which need to emit the VTable in the corresponding
/// object file.
/// A list of classes in named modules which need to emit the VTable in
/// the corresponding object file.
llvm::SmallVector<CXXRecordDecl *> PendingEmittingVTables;

/// Computes input files that didn't affect compilation of the current module,
Expand Down
10 changes: 5 additions & 5 deletions clang/include/clang/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ class InputFile {

InputFile(FileEntryRef File, bool isOverridden = false,
bool isOutOfDate = false) {
assert(!(isOverridden && isOutOfDate) &&
"an overridden cannot be out-of-date");
unsigned intVal = 0;
if (isOverridden)
intVal = Overridden;
else if (isOutOfDate)
// Make isOutOfDate with higher priority than isOverridden.
// It is possible if the recorded hash value mismatches.
if (isOutOfDate)
intVal = OutOfDate;
else if (isOverridden)
intVal = Overridden;
Val.setPointerAndInt(&File.getMapEntry(), intVal);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
// This file defines CheckerVisitor.
// This file defines various utilities used by checkers.
//
//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -114,6 +114,10 @@ OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK,

std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State);

/// Returns true if declaration \p D is in std namespace or any nested namespace
/// or class scope.
bool isWithinStdNamespace(const Decl *D);

} // namespace ento

} // namespace clang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ class ExprEngine {
const Stmt *DiagnosticStmt = nullptr,
ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);

/// A tag to track convenience transitions, which can be removed at cleanup.
/// This tag applies to a node created after removeDead.
static const ProgramPointTag *cleanupNodeTag();

/// processCFGElement - Called by CoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a CFG element.
void processCFGElement(const CFGElement E, ExplodedNode *Pred,
Expand Down
106 changes: 106 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4991,6 +4991,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
const Expr *SubExpr = E->getSubExpr();
if (SubExpr->getType()->isAnyComplexType())
return this->VisitComplexUnaryOperator(E);
if (SubExpr->getType()->isVectorType())
return this->VisitVectorUnaryOperator(E);
std::optional<PrimType> T = classify(SubExpr->getType());

switch (E->getOpcode()) {
Expand Down Expand Up @@ -5312,6 +5314,110 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) {
return true;
}

template <class Emitter>
bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
const Expr *SubExpr = E->getSubExpr();
assert(SubExpr->getType()->isVectorType());

if (DiscardResult)
return this->discard(SubExpr);

auto UnaryOp = E->getOpcode();
if (UnaryOp != UO_Plus && UnaryOp != UO_Minus && UnaryOp != UO_LNot &&
UnaryOp != UO_Not)
return this->emitInvalid(E);

// Nothing to do here.
if (UnaryOp == UO_Plus)
return this->delegate(SubExpr);

if (!Initializing) {
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}

// The offset of the temporary, if we created one.
unsigned SubExprOffset =
this->allocateLocalPrimitive(SubExpr, PT_Ptr, true, false);
if (!this->visit(SubExpr))
return false;
if (!this->emitSetLocal(PT_Ptr, SubExprOffset, E))
return false;

const auto *VecTy = SubExpr->getType()->getAs<VectorType>();
PrimType ElemT = classifyVectorElementType(SubExpr->getType());
auto getElem = [=](unsigned Offset, unsigned Index) -> bool {
if (!this->emitGetLocal(PT_Ptr, Offset, E))
return false;
return this->emitArrayElemPop(ElemT, Index, E);
};

switch (UnaryOp) {
case UO_Minus:
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
if (!getElem(SubExprOffset, I))
return false;
if (!this->emitNeg(ElemT, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
break;
case UO_LNot: { // !x
// In C++, the logic operators !, &&, || are available for vectors. !v is
// equivalent to v == 0.
//
// The result of the comparison is a vector of the same width and number of
// elements as the comparison operands with a signed integral element type.
//
// https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
QualType ResultVecTy = E->getType();
PrimType ResultVecElemT =
classifyPrim(ResultVecTy->getAs<VectorType>()->getElementType());
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
if (!getElem(SubExprOffset, I))
return false;
// operator ! on vectors returns -1 for 'truth', so negate it.
if (!this->emitPrimCast(ElemT, PT_Bool, Ctx.getASTContext().BoolTy, E))
return false;
if (!this->emitInv(E))
return false;
if (!this->emitPrimCast(PT_Bool, ElemT, VecTy->getElementType(), E))
return false;
if (!this->emitNeg(ElemT, E))
return false;
if (ElemT != ResultVecElemT &&
!this->emitPrimCast(ElemT, ResultVecElemT, ResultVecTy, E))
return false;
if (!this->emitInitElem(ResultVecElemT, I, E))
return false;
}
break;
}
case UO_Not: // ~x
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
if (!getElem(SubExprOffset, I))
return false;
if (ElemT == PT_Bool) {
if (!this->emitInv(E))
return false;
} else {
if (!this->emitComp(ElemT, E))
return false;
}
if (!this->emitInitElem(ElemT, I, E))
return false;
}
break;
default:
llvm_unreachable("Unsupported unary operators should be handled up front");
}
return true;
}

template <class Emitter>
bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (DiscardResult)
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool VisitGNUNullExpr(const GNUNullExpr *E);
bool VisitCXXThisExpr(const CXXThisExpr *E);
bool VisitUnaryOperator(const UnaryOperator *E);
bool VisitVectorUnaryOperator(const UnaryOperator *E);
bool VisitComplexUnaryOperator(const UnaryOperator *E);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
Expand Down Expand Up @@ -349,6 +350,11 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
return *this->classify(ElemType);
}

PrimType classifyVectorElementType(QualType T) const {
assert(T->isVectorType());
return *this->classify(T->getAs<VectorType>()->getElementType());
}

bool emitComplexReal(const Expr *SubExpr);
bool emitComplexBoolCast(const Expr *E);
bool emitComplexComparison(const Expr *LHS, const Expr *RHS,
Expand Down
78 changes: 49 additions & 29 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,14 +305,18 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

if (!Ptr.isLive()) {
const auto &Src = S.Current->getSource(OpPC);
bool IsTemp = Ptr.isTemporary();

S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
if (Ptr.isDynamic()) {
S.FFDiag(Src, diag::note_constexpr_access_deleted_object) << AK;
} else {
bool IsTemp = Ptr.isTemporary();
S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;

if (IsTemp)
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
else
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
if (IsTemp)
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
else
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
}

return false;
}
Expand All @@ -323,36 +327,52 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
assert(Desc);

auto IsConstType = [&S](const VarDecl *VD) -> bool {
QualType T = VD->getType();

if (T.isConstant(S.getASTContext()))
return true;

if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11)
return (T->isSignedIntegerOrEnumerationType() ||
T->isUnsignedIntegerOrEnumerationType()) &&
T.isConstQualified();
const auto *D = Desc->asVarDecl();
if (!D || !D->hasGlobalStorage())
return true;

if (T.isConstQualified())
return true;
if (D == S.EvaluatingDecl)
return true;

if (const auto *RT = T->getAs<ReferenceType>())
return RT->getPointeeType().isConstQualified();
if (D->isConstexpr())
return true;

if (const auto *PT = T->getAs<PointerType>())
return PT->getPointeeType().isConstQualified();
QualType T = D->getType();
bool IsConstant = T.isConstant(S.getASTContext());
if (T->isIntegralOrEnumerationType()) {
if (!IsConstant) {
diagnoseNonConstVariable(S, OpPC, D);
return false;
}
return true;
}

return false;
};
if (IsConstant) {
if (S.getLangOpts().CPlusPlus) {
S.CCEDiag(S.Current->getLocation(OpPC),
S.getLangOpts().CPlusPlus11
? diag::note_constexpr_ltor_non_constexpr
: diag::note_constexpr_ltor_non_integral,
1)
<< D << T;
S.Note(D->getLocation(), diag::note_declared_at);
} else {
S.CCEDiag(S.Current->getLocation(OpPC));
}
return true;
}

if (const auto *D = Desc->asVarDecl();
D && D->hasGlobalStorage() && D != S.EvaluatingDecl && !IsConstType(D)) {
diagnoseNonConstVariable(S, OpPC, D);
return false;
if (T->isPointerOrReferenceType()) {
if (!T->getPointeeType().isConstant(S.getASTContext()) ||
!S.getLangOpts().CPlusPlus11) {
diagnoseNonConstVariable(S, OpPC, D);
return false;
}
return true;
}

return true;
diagnoseNonConstVariable(S, OpPC, D);
return false;
}

static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,12 +517,19 @@ inline bool Divc(InterpState &S, CodePtr OpPC) {

// Den = real(RHS)² + imag(RHS)²
T A, B;
if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B))
return false;
if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B)) {
// Ignore overflow here, because that's what the current interpeter does.
}
T Den;
if (T::add(A, B, Bits, &Den))
return false;

if (Compare(Den, Zero) == ComparisonCategoryResult::Equal) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.FFDiag(E, diag::note_expr_divide_by_zero);
return false;
}

// real(Result) = ((real(LHS) * real(RHS)) + (imag(LHS) * imag(RHS))) / Den
T &ResultR = Result.atIndex(0).deref<T>();
T &ResultI = Result.atIndex(1).deref<T>();
Expand Down Expand Up @@ -2537,7 +2544,7 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
return false;

if (Ptr.isRoot() || !Ptr.isUnknownSizeArray() || Ptr.isDummy()) {
if (Ptr.isRoot() || !Ptr.isUnknownSizeArray()) {
S.Stk.push<Pointer>(Ptr.atIndex(0));
return true;
}
Expand Down Expand Up @@ -2623,7 +2630,11 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckCallable(S, OpPC, Func))
return false;

if (Func->hasThisPointer() && S.checkingPotentialConstantExpression())
// FIXME: The isConstructor() check here is not always right. The current
// constant evaluator is somewhat inconsistent in when it allows a function
// call when checking for a constant expression.
if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() &&
!Func->isConstructor())
return false;

if (!CheckCallDepth(S, OpPC))
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ByteCode/InterpBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
Prev = nullptr;
Root = this;

B.IsDynamic = Blk->IsDynamic;

// Transfer pointers.
B.Pointers = Blk->Pointers;
for (Pointer *P = Blk->Pointers; P; P = P->Next)
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ByteCode/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,14 @@ class Pointer {
}
return false;
}
/// Checks if the storage has been dynamically allocated.
bool isDynamic() const {
if (isBlockPointer()) {
assert(asBlockPointer().Pointee);
return asBlockPointer().Pointee->isDynamic();
}
return false;
}
/// Checks if the storage is a static temporary.
bool isStaticTemporary() const { return isStatic() && isTemporary(); }

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ComputeDependence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ ExprDependence clang::computeDependence(BinaryOperator *E) {
ExprDependence clang::computeDependence(ConditionalOperator *E) {
// The type of the conditional operator depends on the type of the conditional
// to support the GCC vector conditional extension. Additionally,
// [temp.dep.expr] does specify state that this should be dependent on ALL sub
// [temp.dep.expr] does specify that this should be dependent on ALL sub
// expressions.
return E->getCond()->getDependence() | E->getLHS()->getDependence() |
E->getRHS()->getDependence();
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Basic/Targets/DirectX.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo {
BuiltinVaListKind getBuiltinVaListKind() const override {
return TargetInfo::VoidPtrBuiltinVaList;
}

void adjust(DiagnosticsEngine &Diags, LangOptions &Opts) override {
TargetInfo::adjust(Diags, Opts);
// The static values this addresses do not apply outside of the same thread
// This protection is neither available nor needed
Opts.ThreadsafeStatics = false;
}
};

} // namespace targets
Expand Down
6 changes: 2 additions & 4 deletions clang/lib/CodeGen/CGBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "CodeGenTypeCache.h"
#include "llvm/Analysis/Utils/Local.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GEPNoWrapFlags.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Type.h"

Expand Down Expand Up @@ -335,10 +334,9 @@ class CGBuilderTy : public CGBuilderBaseTy {

Address CreateGEP(Address Addr, ArrayRef<llvm::Value *> IdxList,
llvm::Type *ElementType, CharUnits Align,
const Twine &Name = "",
llvm::GEPNoWrapFlags NW = llvm::GEPNoWrapFlags::none()) {
const Twine &Name = "") {
llvm::Value *Ptr = emitRawPointerFromAddress(Addr);
return RawAddress(CreateGEP(Addr.getElementType(), Ptr, IdxList, Name, NW),
return RawAddress(CreateGEP(Addr.getElementType(), Ptr, IdxList, Name),
ElementType, Align);
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20344,6 +20344,7 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
case NVPTX::BI__nvvm_atom_min_gen_ull:
return MakeBinaryAtomicValue(*this, llvm::AtomicRMWInst::UMin, E);

case NVPTX::BI__nvvm_atom_cas_gen_us:
case NVPTX::BI__nvvm_atom_cas_gen_i:
case NVPTX::BI__nvvm_atom_cas_gen_l:
case NVPTX::BI__nvvm_atom_cas_gen_ll:
Expand Down Expand Up @@ -20535,6 +20536,7 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
case NVPTX::BI__nvvm_atom_sys_xor_gen_l:
case NVPTX::BI__nvvm_atom_sys_xor_gen_ll:
return MakeScopedAtomic(Intrinsic::nvvm_atomic_xor_gen_i_sys, *this, E);
case NVPTX::BI__nvvm_atom_cta_cas_gen_us:
case NVPTX::BI__nvvm_atom_cta_cas_gen_i:
case NVPTX::BI__nvvm_atom_cta_cas_gen_l:
case NVPTX::BI__nvvm_atom_cta_cas_gen_ll: {
Expand All @@ -20546,6 +20548,7 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
Intrinsic::nvvm_atomic_cas_gen_i_cta, {ElemTy, Ptr->getType()}),
{Ptr, EmitScalarExpr(E->getArg(1)), EmitScalarExpr(E->getArg(2))});
}
case NVPTX::BI__nvvm_atom_sys_cas_gen_us:
case NVPTX::BI__nvvm_atom_sys_cas_gen_i:
case NVPTX::BI__nvvm_atom_sys_cas_gen_l:
case NVPTX::BI__nvvm_atom_sys_cas_gen_ll: {
Expand Down
17 changes: 3 additions & 14 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/FixedPointBuilder.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GEPNoWrapFlags.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Intrinsics.h"
Expand Down Expand Up @@ -5757,12 +5756,7 @@ CodeGenFunction::EmitCheckedInBoundsGEP(llvm::Type *ElemTy, Value *Ptr,
bool SignedIndices, bool IsSubtraction,
SourceLocation Loc, const Twine &Name) {
llvm::Type *PtrTy = Ptr->getType();

llvm::GEPNoWrapFlags NWFlags = llvm::GEPNoWrapFlags::inBounds();
if (!SignedIndices && !IsSubtraction)
NWFlags |= llvm::GEPNoWrapFlags::noUnsignedWrap();

Value *GEPVal = Builder.CreateGEP(ElemTy, Ptr, IdxList, Name, NWFlags);
Value *GEPVal = Builder.CreateInBoundsGEP(ElemTy, Ptr, IdxList, Name);

// If the pointer overflow sanitizer isn't enabled, do nothing.
if (!SanOpts.has(SanitizerKind::PointerOverflow))
Expand Down Expand Up @@ -5877,13 +5871,8 @@ Address CodeGenFunction::EmitCheckedInBoundsGEP(
Address Addr, ArrayRef<Value *> IdxList, llvm::Type *elementType,
bool SignedIndices, bool IsSubtraction, SourceLocation Loc, CharUnits Align,
const Twine &Name) {
if (!SanOpts.has(SanitizerKind::PointerOverflow)) {
llvm::GEPNoWrapFlags NWFlags = llvm::GEPNoWrapFlags::inBounds();
if (!SignedIndices && !IsSubtraction)
NWFlags |= llvm::GEPNoWrapFlags::noUnsignedWrap();

return Builder.CreateGEP(Addr, IdxList, elementType, Align, Name, NWFlags);
}
if (!SanOpts.has(SanitizerKind::PointerOverflow))
return Builder.CreateInBoundsGEP(Addr, IdxList, elementType, Align, Name);

return RawAddress(
EmitCheckedInBoundsGEP(Addr.getElementType(), Addr.emitRawPointer(*this),
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/CodeGen/CodeGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,9 +969,10 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const {
return BEConsumer->getCodeGenerator();
}

bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) {
bool CodeGenAction::BeginInvocation(CompilerInstance &CI) {
if (CI.getFrontendOpts().GenReducedBMI)
CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
return BeginInvocationForModules(CI);

return true;
}

Expand Down
24 changes: 12 additions & 12 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4455,12 +4455,13 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
if (getTarget().supportsIFunc()) {
llvm::GlobalValue::LinkageTypes Linkage = getMultiversionLinkage(*this, GD);
auto *IFunc = cast<llvm::GlobalValue>(GetOrCreateMultiVersionResolver(GD));
unsigned AS = IFunc->getType()->getPointerAddressSpace();

// Fix up function declarations that were created for cpu_specific before
// cpu_dispatch was known
if (!isa<llvm::GlobalIFunc>(IFunc)) {
auto *GI = llvm::GlobalIFunc::create(DeclTy, 0, Linkage, "", ResolverFunc,
&getModule());
auto *GI = llvm::GlobalIFunc::create(DeclTy, AS, Linkage, "",
ResolverFunc, &getModule());
replaceDeclarationWith(IFunc, GI);
IFunc = GI;
}
Expand All @@ -4469,8 +4470,8 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) {
*this, GD, FD, /*OmitMultiVersionMangling=*/true);
llvm::Constant *AliasFunc = GetGlobalValue(AliasName);
if (!AliasFunc) {
auto *GA = llvm::GlobalAlias::create(DeclTy, 0, Linkage, AliasName, IFunc,
&getModule());
auto *GA = llvm::GlobalAlias::create(DeclTy, AS, Linkage, AliasName,
IFunc, &getModule());
SetCommonAttributes(GD, GA);
}
}
Expand Down Expand Up @@ -4542,15 +4543,14 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) {
// For cpu_specific, don't create an ifunc yet because we don't know if the
// cpu_dispatch will be emitted in this translation unit.
if (getTarget().supportsIFunc() && !FD->isCPUSpecificMultiVersion()) {
llvm::Type *ResolverType = llvm::FunctionType::get(
llvm::PointerType::get(DeclTy,
getTypes().getTargetAddressSpace(FD->getType())),
false);
unsigned AS = getTypes().getTargetAddressSpace(FD->getType());
llvm::Type *ResolverType =
llvm::FunctionType::get(llvm::PointerType::get(DeclTy, AS), false);
llvm::Constant *Resolver = GetOrCreateLLVMFunction(
MangledName + ".resolver", ResolverType, GlobalDecl{},
/*ForVTable=*/false);
llvm::GlobalIFunc *GIF =
llvm::GlobalIFunc::create(DeclTy, 0, getMultiversionLinkage(*this, GD),
llvm::GlobalIFunc::create(DeclTy, AS, getMultiversionLinkage(*this, GD),
"", Resolver, &getModule());
GIF->setName(ResolverName);
SetCommonAttributes(FD, GIF);
Expand Down Expand Up @@ -6160,9 +6160,9 @@ void CodeGenModule::emitIFuncDefinition(GlobalDecl GD) {
GetOrCreateLLVMFunction(IFA->getResolver(), VoidTy, {},
/*ForVTable=*/false);
llvm::Type *DeclTy = getTypes().ConvertTypeForMem(D->getType());
llvm::GlobalIFunc *GIF =
llvm::GlobalIFunc::create(DeclTy, 0, llvm::Function::ExternalLinkage,
"", Resolver, &getModule());
unsigned AS = getTypes().getTargetAddressSpace(D->getType());
llvm::GlobalIFunc *GIF = llvm::GlobalIFunc::create(
DeclTy, AS, llvm::Function::ExternalLinkage, "", Resolver, &getModule());
if (Entry) {
if (GIF->getResolver() == Entry) {
Diags.Report(IFA->getLocation(), diag::err_cyclic_alias) << 1;
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ static void getAArch64MultilibFlags(const Driver &D,
assert(!ArchName.empty() && "at least one architecture should be found");
MArch.insert(MArch.begin(), ("-march=" + ArchName).str());
Result.push_back(llvm::join(MArch, "+"));

const Arg *BranchProtectionArg =
Args.getLastArgNoClaim(options::OPT_mbranch_protection_EQ);
if (BranchProtectionArg) {
Result.push_back(BranchProtectionArg->getAsString(Args));
}
}

static void getARMMultilibFlags(const Driver &D,
Expand Down Expand Up @@ -268,6 +274,12 @@ static void getARMMultilibFlags(const Driver &D,
case arm::FloatABI::Invalid:
llvm_unreachable("Invalid float ABI");
}

const Arg *BranchProtectionArg =
Args.getLastArgNoClaim(options::OPT_mbranch_protection_EQ);
if (BranchProtectionArg) {
Result.push_back(BranchProtectionArg->getAsString(Args));
}
}

static void getRISCVMultilibFlags(const Driver &D, const llvm::Triple &Triple,
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/ExtractAPI/API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,13 @@ void APISet::removeRecord(StringRef USR) {
if (auto *RecordAsCtx = llvm::dyn_cast<RecordContext>(Record))
ParentCtx->stealRecordChain(*RecordAsCtx);
} else {
TopLevelRecords.erase(Record);
auto *It = llvm::find(TopLevelRecords, Record);
if (It != TopLevelRecords.end())
TopLevelRecords.erase(It);
if (auto *RecordAsCtx = llvm::dyn_cast<RecordContext>(Record)) {
for (const auto *Child = RecordAsCtx->First; Child != nullptr;
Child = Child->getNextInContext())
TopLevelRecords.insert(Child);
TopLevelRecords.push_back(Child);
}
}
USRBasedLookupTable.erase(Result);
Expand Down
10 changes: 4 additions & 6 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -894,10 +894,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
CurrentState.ContainsUnwrappedBuilder = true;
}

if (Current.is(TT_TrailingReturnArrow) &&
Style.Language == FormatStyle::LK_Java) {
if (Current.is(TT_LambdaArrow) && Style.Language == FormatStyle::LK_Java)
CurrentState.NoLineBreak = true;
}
if (Current.isMemberAccess() && Previous.is(tok::r_paren) &&
(Previous.MatchingParen &&
(Previous.TotalLength - Previous.MatchingParen->TotalLength > 10))) {
Expand Down Expand Up @@ -1052,7 +1050,7 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
//
// is common and should be formatted like a free-standing function. The same
// goes for wrapping before the lambda return type arrow.
if (Current.isNot(TT_TrailingReturnArrow) &&
if (Current.isNot(TT_LambdaArrow) &&
(!Style.isJavaScript() || Current.NestingLevel != 0 ||
!PreviousNonComment || PreviousNonComment->isNot(tok::equal) ||
!Current.isOneOf(Keywords.kw_async, Keywords.kw_function))) {
Expand Down Expand Up @@ -1312,7 +1310,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
}
return CurrentState.Indent;
}
if (Current.is(TT_TrailingReturnArrow) &&
if (Current.is(TT_LambdaArrow) &&
Previous.isOneOf(tok::kw_noexcept, tok::kw_mutable, tok::kw_constexpr,
tok::kw_consteval, tok::kw_static, TT_AttributeSquare)) {
return ContinuationIndent;
Expand Down Expand Up @@ -1646,7 +1644,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
}
if (Current.isOneOf(TT_BinaryOperator, TT_ConditionalExpr) && Newline)
CurrentState.NestedBlockIndent = State.Column + Current.ColumnWidth + 1;
if (Current.isOneOf(TT_LambdaLSquare, TT_TrailingReturnArrow))
if (Current.isOneOf(TT_LambdaLSquare, TT_LambdaArrow))
CurrentState.LastSpace = State.Column;
if (Current.is(TT_RequiresExpression) &&
Style.RequiresExpressionIndentation == FormatStyle::REI_Keyword) {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Format/FormatToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace format {
TYPE(JsTypeColon) \
TYPE(JsTypeOperator) \
TYPE(JsTypeOptionalQuestion) \
TYPE(LambdaArrow) \
TYPE(LambdaDefinitionLParen) \
TYPE(LambdaLBrace) \
TYPE(LambdaLSquare) \
Expand Down Expand Up @@ -726,7 +727,7 @@ struct FormatToken {
bool isMemberAccess() const {
return isOneOf(tok::arrow, tok::period, tok::arrowstar) &&
!isOneOf(TT_DesignatedInitializerPeriod, TT_TrailingReturnArrow,
TT_LeadingJavaAnnotation);
TT_LambdaArrow, TT_LeadingJavaAnnotation);
}

bool isPointerOrReference() const {
Expand Down
37 changes: 21 additions & 16 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ class AnnotatingParser {
}
// An arrow after an ObjC method expression is not a lambda arrow.
if (CurrentToken->is(TT_ObjCMethodExpr) && CurrentToken->Next &&
CurrentToken->Next->is(TT_TrailingReturnArrow)) {
CurrentToken->Next->is(TT_LambdaArrow)) {
CurrentToken->Next->overwriteFixedType(TT_Unknown);
}
Left->MatchingParen = CurrentToken;
Expand Down Expand Up @@ -1770,8 +1770,10 @@ class AnnotatingParser {
}
break;
case tok::arrow:
if (Tok->Previous && Tok->Previous->is(tok::kw_noexcept))
if (Tok->isNot(TT_LambdaArrow) && Tok->Previous &&
Tok->Previous->is(tok::kw_noexcept)) {
Tok->setType(TT_TrailingReturnArrow);
}
break;
case tok::equal:
// In TableGen, there must be a value after "=";
Expand Down Expand Up @@ -2057,11 +2059,11 @@ class AnnotatingParser {
TT_LambdaLSquare, TT_LambdaLBrace, TT_AttributeMacro, TT_IfMacro,
TT_ForEachMacro, TT_TypenameMacro, TT_FunctionLBrace,
TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_FatArrow,
TT_NamespaceMacro, TT_OverloadedOperator, TT_RegexLiteral,
TT_TemplateString, TT_ObjCStringLiteral, TT_UntouchableMacroFunc,
TT_StatementAttributeLikeMacro, TT_FunctionLikeOrFreestandingMacro,
TT_ClassLBrace, TT_EnumLBrace, TT_RecordLBrace, TT_StructLBrace,
TT_UnionLBrace, TT_RequiresClause,
TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator,
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral,
TT_UntouchableMacroFunc, TT_StatementAttributeLikeMacro,
TT_FunctionLikeOrFreestandingMacro, TT_ClassLBrace, TT_EnumLBrace,
TT_RecordLBrace, TT_StructLBrace, TT_UnionLBrace, TT_RequiresClause,
TT_RequiresClauseInARequiresExpression, TT_RequiresExpression,
TT_RequiresExpressionLParen, TT_RequiresExpressionLBrace,
TT_BracedListLBrace)) {
Expand Down Expand Up @@ -2247,7 +2249,7 @@ class AnnotatingParser {
Contexts.back().IsExpression = true;
} else if (Current.is(TT_TrailingReturnArrow)) {
Contexts.back().IsExpression = false;
} else if (Current.is(Keywords.kw_assert)) {
} else if (Current.isOneOf(TT_LambdaArrow, Keywords.kw_assert)) {
Contexts.back().IsExpression = Style.Language == FormatStyle::LK_Java;
} else if (Current.Previous &&
Current.Previous->is(TT_CtorInitializerColon)) {
Expand Down Expand Up @@ -2382,7 +2384,7 @@ class AnnotatingParser {
AutoFound = true;
} else if (Current.is(tok::arrow) &&
Style.Language == FormatStyle::LK_Java) {
Current.setType(TT_TrailingReturnArrow);
Current.setType(TT_LambdaArrow);
} else if (Current.is(tok::arrow) && Style.isVerilog()) {
// The implication operator.
Current.setType(TT_BinaryOperator);
Expand Down Expand Up @@ -3288,7 +3290,7 @@ class ExpressionParser {
}
if (Current->is(TT_JsComputedPropertyName))
return prec::Assignment;
if (Current->is(TT_TrailingReturnArrow))
if (Current->is(TT_LambdaArrow))
return prec::Comma;
if (Current->is(TT_FatArrow))
return prec::Assignment;
Expand Down Expand Up @@ -4213,7 +4215,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line,
}
if (Right.is(TT_PointerOrReference))
return 190;
if (Right.is(TT_TrailingReturnArrow))
if (Right.is(TT_LambdaArrow))
return 110;
if (Left.is(tok::equal) && Right.is(tok::l_brace))
return 160;
Expand Down Expand Up @@ -4732,7 +4734,9 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
Left.isOneOf(tok::kw_new, tok::kw_delete) &&
Right.isNot(TT_OverloadedOperatorLParen) &&
!(Line.MightBeFunctionDecl && Left.is(TT_FunctionDeclarationName))) {
return Style.SpaceBeforeParensOptions.AfterPlacementOperator;
const auto *RParen = Right.MatchingParen;
return Style.SpaceBeforeParensOptions.AfterPlacementOperator ||
(RParen && RParen->is(TT_CastRParen));
}
if (Line.Type == LT_ObjCDecl)
return true;
Expand Down Expand Up @@ -5291,9 +5295,10 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
return false;
}

if (Right.is(TT_TrailingReturnArrow) || Left.is(TT_TrailingReturnArrow))
if (Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow) ||
Left.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow)) {
return true;

}
if (Left.is(tok::comma) && Right.isNot(TT_OverloadedOperatorLParen) &&
// In an unexpanded macro call we only find the parentheses and commas
// in a line; the commas and closing parenthesis do not require a space.
Expand Down Expand Up @@ -6328,8 +6333,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
return Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace,
tok::kw_class, tok::kw_struct, tok::comment) ||
Right.isMemberAccess() ||
Right.isOneOf(TT_TrailingReturnArrow, tok::lessless, tok::colon,
tok::l_square, tok::at) ||
Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow, tok::lessless,
tok::colon, tok::l_square, tok::at) ||
(Left.is(tok::r_paren) &&
Right.isOneOf(tok::identifier, tok::kw_const)) ||
(Left.is(tok::l_paren) && Right.isNot(tok::r_paren)) ||
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2326,7 +2326,7 @@ bool UnwrappedLineParser::tryToParseLambda() {
// This might or might not actually be a lambda arrow (this could be an
// ObjC method invocation followed by a dereferencing arrow). We might
// reset this back to TT_Unknown in TokenAnnotator.
FormatTok->setFinalizedType(TT_TrailingReturnArrow);
FormatTok->setFinalizedType(TT_LambdaArrow);
SeenArrow = true;
nextToken();
break;
Expand Down Expand Up @@ -3992,6 +3992,9 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
auto IsNonMacroIdentifier = [](const FormatToken *Tok) {
return Tok->is(tok::identifier) && Tok->TokenText != Tok->TokenText.upper();
};
// JavaScript/TypeScript supports anonymous classes like:
// a = class extends foo { }
bool JSPastExtendsOrImplements = false;
// The actual identifier can be a nested name specifier, and in macros
// it is often token-pasted.
// An [[attribute]] can be before the identifier.
Expand All @@ -4002,6 +4005,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
FormatTok->isOneOf(tok::period, tok::comma))) {
if (Style.isJavaScript() &&
FormatTok->isOneOf(Keywords.kw_extends, Keywords.kw_implements)) {
JSPastExtendsOrImplements = true;
// JavaScript/TypeScript supports inline object types in
// extends/implements positions:
// class Foo implements {bar: number} { }
Expand All @@ -4027,8 +4031,8 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
case tok::coloncolon:
break;
default:
if (!ClassName && Previous->is(tok::identifier) &&
Previous->isNot(TT_AttributeMacro)) {
if (!JSPastExtendsOrImplements && !ClassName &&
Previous->is(tok::identifier) && Previous->isNot(TT_AttributeMacro)) {
ClassName = Previous;
}
}
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Format/WhitespaceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// except if the token is equal, then a space is needed.
if ((Style.PointerAlignment == FormatStyle::PAS_Right ||
Style.ReferenceAlignment == FormatStyle::RAS_Right) &&
CurrentChange.Spaces != 0 && CurrentChange.Tok->isNot(tok::equal)) {
CurrentChange.Spaces != 0 &&
!CurrentChange.Tok->isOneOf(tok::equal, tok::r_paren,
TT_TemplateCloser)) {
const bool ReferenceNotRightAligned =
Style.ReferenceAlignment != FormatStyle::RAS_Right &&
Style.ReferenceAlignment != FormatStyle::RAS_Pointer;
Expand Down
15 changes: 12 additions & 3 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,20 @@ GenerateModuleFromModuleMapAction::CreateOutputFile(CompilerInstance &CI,
/*ForceUseTemporary=*/true);
}

bool GenerateModuleInterfaceAction::BeginSourceFileAction(
CompilerInstance &CI) {
bool clang::BeginInvocationForModules(CompilerInstance &CI) {
// Embed all module files for named modules.
// See https://github.com/llvm/llvm-project/issues/72383 for discussion.
CI.getFrontendOpts().ModulesEmbedAllFiles = true;
CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
return true;
}

return GenerateModuleAction::BeginSourceFileAction(CI);
bool GenerateModuleInterfaceAction::BeginInvocation(
CompilerInstance &CI) {
if (!BeginInvocationForModules(CI))
return false;

return GenerateModuleAction::BeginInvocation(CI);
}

std::unique_ptr<ASTConsumer>
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Headers/__clang_cuda_device_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,20 @@ __DEVICE__ void __threadfence(void) { __nvvm_membar_gl(); }
__DEVICE__ void __threadfence_block(void) { __nvvm_membar_cta(); };
__DEVICE__ void __threadfence_system(void) { __nvvm_membar_sys(); };
__DEVICE__ void __trap(void) { __asm__ __volatile__("trap;"); }
__DEVICE__ unsigned short
__usAtomicCAS(unsigned short *__p, unsigned short __cmp, unsigned short __v) {
return __nvvm_atom_cas_gen_us(__p, __cmp, __v);
}
__DEVICE__ unsigned short __usAtomicCAS_block(unsigned short *__p,
unsigned short __cmp,
unsigned short __v) {
return __nvvm_atom_cta_cas_gen_us(__p, __cmp, __v);
}
__DEVICE__ unsigned short __usAtomicCAS_system(unsigned short *__p,
unsigned short __cmp,
unsigned short __v) {
return __nvvm_atom_sys_cas_gen_us(__p, __cmp, __v);
}
__DEVICE__ unsigned int __uAtomicAdd(unsigned int *__p, unsigned int __v) {
return __nvvm_atom_add_gen_i((int *)__p, __v);
}
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2954,8 +2954,6 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
}
break;
}
case OMPD_reverse:
case OMPD_interchange:
case OMPD_declare_target: {
SourceLocation DTLoc = ConsumeAnyToken();
bool HasClauses = Tok.isNot(tok::annot_pragma_openmp_end);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/MultiplexExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ bool MultiplexExternalSemaSource::MaybeDiagnoseMissingCompleteType(
}

void MultiplexExternalSemaSource::AssignedLambdaNumbering(
const CXXRecordDecl *Lambda) {
CXXRecordDecl *Lambda) {
for (auto *Source : Sources)
Source->AssignedLambdaNumbering(Lambda);
}
53 changes: 53 additions & 0 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,59 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
inferGslPointerAttribute(Record, Record);
}

void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
if (FD->getNumParams() == 0)
return;

if (unsigned BuiltinID = FD->getBuiltinID()) {
// Add lifetime attribute to std::move, std::fowrard et al.
switch (BuiltinID) {
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
case Builtin::BIas_const:
case Builtin::BIforward:
case Builtin::BIforward_like:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
if (ParmVarDecl *P = FD->getParamDecl(0u);
!P->hasAttr<LifetimeBoundAttr>())
P->addAttr(
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
break;
default:
break;
}
return;
}
if (auto *CMD = dyn_cast<CXXMethodDecl>(FD)) {
const auto *CRD = CMD->getParent();
if (!CRD->isInStdNamespace() || !CRD->getIdentifier())
return;

if (isa<CXXConstructorDecl>(CMD)) {
auto *Param = CMD->getParamDecl(0);
if (Param->hasAttr<LifetimeBoundAttr>())
return;
if (CRD->getName() == "basic_string_view" &&
Param->getType()->isPointerType()) {
// construct from a char array pointed by a pointer.
// basic_string_view(const CharT* s);
// basic_string_view(const CharT* s, size_type count);
Param->addAttr(
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
} else if (CRD->getName() == "span") {
// construct from a reference of array.
// span(std::type_identity_t<element_type> (&arr)[N]);
const auto *LRT = Param->getType()->getAs<LValueReferenceType>();
if (LRT && LRT->getPointeeType().IgnoreParens()->isArrayType())
Param->addAttr(
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
}
}
}
}

void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
static const llvm::StringSet<> Nullable{
"auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr",
Expand Down
20 changes: 1 addition & 19 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16608,27 +16608,9 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
default:
break;
}

// Add lifetime attribute to std::move, std::fowrard et al.
switch (BuiltinID) {
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
case Builtin::BIas_const:
case Builtin::BIforward:
case Builtin::BIforward_like:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
if (ParmVarDecl *P = FD->getParamDecl(0u);
!P->hasAttr<LifetimeBoundAttr>())
P->addAttr(
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
break;
default:
break;
}
}

inferLifetimeBoundAttribute(FD);
AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);

// If C++ exceptions are enabled but we are told extern "C" functions cannot
Expand Down
828 changes: 571 additions & 257 deletions clang/lib/Sema/SemaTemplateDeduction.cpp

Large diffs are not rendered by default.

51 changes: 40 additions & 11 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1344,15 +1344,21 @@ namespace {
DeclarationName Entity;
// Whether to evaluate the C++20 constraints or simply substitute into them.
bool EvaluateConstraints = true;
// Whether Substitution was Incomplete, that is, we tried to substitute in
// any user provided template arguments which were null.
bool IsIncomplete = false;
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;

public:
typedef TreeTransform<TemplateInstantiator> inherited;

TemplateInstantiator(Sema &SemaRef,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity)
SourceLocation Loc, DeclarationName Entity,
bool BailOutOnIncomplete = false)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
Entity(Entity) {}
Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}

void setEvaluateConstraints(bool B) {
EvaluateConstraints = B;
Expand All @@ -1374,6 +1380,9 @@ namespace {
/// Returns the name of the entity being instantiated, if any.
DeclarationName getBaseEntity() { return Entity; }

/// Returns whether any substitution so far was incomplete.
bool getIsIncomplete() const { return IsIncomplete; }

/// Sets the "base" location and entity when that
/// information is known based on another transformation.
void setBase(SourceLocation Loc, DeclarationName Entity) {
Expand Down Expand Up @@ -1420,6 +1429,10 @@ namespace {
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
Result = TemplateArgs(Depth, Index);
TemplateArgs.setArgument(Depth, Index, TemplateArgument());
} else {
IsIncomplete = true;
if (BailOutOnIncomplete)
return TemplateArgument();
}
}

Expand Down Expand Up @@ -1820,8 +1833,10 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
TTP->getPosition()))
return D;
TTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? nullptr : D;
}

TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());

Expand Down Expand Up @@ -1967,8 +1982,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
TTP->getPosition()))
return Name;
TTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? TemplateName() : Name;
}

TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());

Expand Down Expand Up @@ -2050,8 +2067,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
// template arguments in a function template, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(),
NTTP->getPosition()))
return E;
NTTP->getPosition())) {
IsIncomplete = true;
return BailOutOnIncomplete ? ExprError() : E;
}

TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());

Expand Down Expand Up @@ -2470,6 +2489,10 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
// template arguments in a function template class, but there were some
// arguments left unspecified.
if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex())) {
IsIncomplete = true;
if (BailOutOnIncomplete)
return QualType();

TemplateTypeParmTypeLoc NewTL
= TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
NewTL.setNameLoc(TL.getNameLoc());
Expand Down Expand Up @@ -2841,7 +2864,8 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
/// Deprecated form of the above.
QualType Sema::SubstType(QualType T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity) {
SourceLocation Loc, DeclarationName Entity,
bool *IsIncompleteSubstitution) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
Expand All @@ -2851,8 +2875,13 @@ QualType Sema::SubstType(QualType T,
if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType())
return T;

TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
return Instantiator.TransformType(T);
TemplateInstantiator Instantiator(
*this, TemplateArgs, Loc, Entity,
/*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr);
QualType QT = Instantiator.TransformType(T);
if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
*IsIncompleteSubstitution = true;
return QT;
}

static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {
Expand Down
35 changes: 27 additions & 8 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8963,15 +8963,34 @@ void ASTReader::ReadLateParsedTemplates(
LateParsedTemplates.clear();
}

void ASTReader::AssignedLambdaNumbering(const CXXRecordDecl *Lambda) {
if (Lambda->getLambdaContextDecl()) {
// Keep track of this lambda so it can be merged with another lambda that
// is loaded later.
LambdaDeclarationsForMerging.insert(
{{Lambda->getLambdaContextDecl()->getCanonicalDecl(),
Lambda->getLambdaIndexInContext()},
const_cast<CXXRecordDecl *>(Lambda)});
void ASTReader::AssignedLambdaNumbering(CXXRecordDecl *Lambda) {
if (!Lambda->getLambdaContextDecl())
return;

auto LambdaInfo =
std::make_pair(Lambda->getLambdaContextDecl()->getCanonicalDecl(),
Lambda->getLambdaIndexInContext());

// Handle the import and then include case for lambdas.
if (auto Iter = LambdaDeclarationsForMerging.find(LambdaInfo);
Iter != LambdaDeclarationsForMerging.end() &&
Iter->second->isFromASTFile() && Lambda->getFirstDecl() == Lambda) {
CXXRecordDecl *Previous =
cast<CXXRecordDecl>(Iter->second)->getMostRecentDecl();
Lambda->setPreviousDecl(Previous);
// FIXME: It will be best to use the Previous type when we creating the
// lambda directly. But that requires us to get the lambda context decl and
// lambda index before creating the lambda, which needs a drastic change in
// the parser.
const_cast<QualType &>(Lambda->TypeForDecl->CanonicalType) =
Previous->TypeForDecl->CanonicalType;
return;
}

// Keep track of this lambda so it can be merged with another lambda that
// is loaded later.
LambdaDeclarationsForMerging.insert(
{LambdaInfo, const_cast<CXXRecordDecl *>(Lambda)});
}

void ASTReader::LoadSelector(Selector Sel) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3963,6 +3963,9 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP,
}

void ASTWriter::handleVTable(CXXRecordDecl *RD) {
if (!RD->isInNamedModule())
return;

PendingEmittingVTables.push_back(RD);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
Expand Down Expand Up @@ -241,12 +242,22 @@ BlockInCriticalSectionChecker::checkDescriptorMatch(const CallEvent &Call,
return std::nullopt;
}

static const MemRegion *skipStdBaseClassRegion(const MemRegion *Reg) {
while (Reg) {
const auto *BaseClassRegion = dyn_cast<CXXBaseObjectRegion>(Reg);
if (!BaseClassRegion || !isWithinStdNamespace(BaseClassRegion->getDecl()))
break;
Reg = BaseClassRegion->getSuperRegion();
}
return Reg;
}

static const MemRegion *getRegion(const CallEvent &Call,
const MutexDescriptor &Descriptor,
bool IsLock) {
return std::visit(
[&Call, IsLock](auto &&Descriptor) {
return Descriptor.getRegion(Call, IsLock);
[&Call, IsLock](auto &Descr) -> const MemRegion * {
return skipStdBaseClassRegion(Descr.getRegion(Call, IsLock));
},
Descriptor);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
"Storage provided to placement new is only {0} bytes, "
"whereas the allocated array type requires more space for "
"internal needs",
SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
SizeOfPlaceCI->getValue()));
else
Msg = std::string(llvm::formatv(
"Storage provided to placement new is only {0} bytes, "
Expand Down
78 changes: 72 additions & 6 deletions clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,14 @@ static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {
return nullptr;
}

const MemRegion *getOriginBaseRegion(const MemRegion *Reg) {
Reg = Reg->getBaseRegion();
while (const auto *SymReg = dyn_cast<SymbolicRegion>(Reg)) {
Reg = SymReg->getSymbol()->getOriginRegion()->getBaseRegion();
}
return Reg;
}

std::optional<std::string> printReferrer(const MemRegion *Referrer) {
assert(Referrer);
const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
Expand All @@ -313,7 +321,8 @@ std::optional<std::string> printReferrer(const MemRegion *Referrer) {
if (isa<GlobalsSpaceRegion>(Space))
return "global";
assert(isa<StackSpaceRegion>(Space));
return "stack";
// This case covers top-level and inlined analyses.
return "caller";
}(getStackOrGlobalSpaceRegion(Referrer));

while (!Referrer->canPrintPretty()) {
Expand All @@ -340,19 +349,45 @@ std::optional<std::string> printReferrer(const MemRegion *Referrer) {
return buf;
}

/// Check whether \p Region refers to a freshly minted symbol after an opaque
/// function call.
bool isInvalidatedSymbolRegion(const MemRegion *Region) {
const auto *SymReg = Region->getAs<SymbolicRegion>();
if (!SymReg)
return false;
SymbolRef Symbol = SymReg->getSymbol();

const auto *DerS = dyn_cast<SymbolDerived>(Symbol);
return DerS && isa_and_nonnull<SymbolConjured>(DerS->getParentSymbol());
}

void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
return;

ExplodedNode *Node = Ctx.getPredecessor();

bool ExitingTopFrame =
Ctx.getPredecessor()->getLocationContext()->inTopFrame();

if (ExitingTopFrame &&
Node->getLocation().getTag() == ExprEngine::cleanupNodeTag() &&
Node->getFirstPred()) {
// When finishing analysis of a top-level function, engine proactively
// removes dead symbols thus preventing this checker from looking through
// the output parameters. Take 1 step back, to the node where these symbols
// and their bindings are still present
Node = Node->getFirstPred();
}

// Iterate over all bindings to global variables and see if it contains
// a memory region in the stack space.
class CallBack : public StoreManager::BindingsHandler {
private:
CheckerContext &Ctx;
const StackFrameContext *PoppedFrame;
const bool TopFrame;

/// Look for stack variables referring to popped stack variables.
/// Returns true only if it found some dangling stack variables
Expand All @@ -368,24 +403,51 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,

const auto *ReferrerStackSpace =
ReferrerMemSpace->getAs<StackSpaceRegion>();

if (!ReferrerStackSpace)
return false;

if (ReferredMemSpace->getStackFrame() == PoppedFrame &&
ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
ReferredFrame != PoppedFrame) {
return false;
}

if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
V.emplace_back(Referrer, Referred);
return true;
}
if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) &&
ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
// Output parameter of a top-level function
V.emplace_back(Referrer, Referred);
return true;
}
return false;
}

// Keep track of the variables that were invalidated through an opaque
// function call. Even if the initial values of such variables were bound to
// an address of a local variable, we cannot claim anything now, at the
// function exit, so skip them to avoid false positives.
void recordInInvalidatedRegions(const MemRegion *Region) {
if (isInvalidatedSymbolRegion(Region))
ExcludedRegions.insert(getOriginBaseRegion(Region));
}

public:
SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
// ExcludedRegions are skipped from reporting.
// I.e., if a referrer in this set, skip the related bug report.
// It is useful to avoid false positive for the variables that were
// reset to a conjured value after an opaque function call.
llvm::SmallPtrSet<const MemRegion *, 4> ExcludedRegions;

CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
CallBack(CheckerContext &CC, bool TopFrame)
: Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {}

bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
SVal Val) override {
recordInInvalidatedRegions(Region);
const MemRegion *VR = Val.getAsRegion();
if (!VR)
return true;
Expand All @@ -394,15 +456,16 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return true;

// Check the globals for the same.
if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
if (!isa_and_nonnull<GlobalsSpaceRegion>(
getStackOrGlobalSpaceRegion(Region)))
return true;
if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
V.emplace_back(Region, VR);
return true;
}
};

CallBack Cb(Ctx);
CallBack Cb(Ctx, ExitingTopFrame);
ProgramStateRef State = Node->getState();
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Cb);
Expand All @@ -423,6 +486,9 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
for (const auto &P : Cb.V) {
const MemRegion *Referrer = P.first->getBaseRegion();
const MemRegion *Referred = P.second;
if (Cb.ExcludedRegions.contains(getOriginBaseRegion(Referrer))) {
continue;
}

// Generate a report for this bug.
const StringRef CommonSuffix =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1401,7 +1401,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
ErrnoNote =
llvm::formatv("After calling '{0}' {1}", FunctionName, ErrnoNote);
} else {
CaseNote = llvm::formatv(Case.getNote().str().c_str(), FunctionName);
// Disable formatv() validation as the case note may not always have the
// {0} placeholder for function name.
CaseNote =
llvm::formatv(false, Case.getNote().str().c_str(), FunctionName);
}
const SVal RV = Call.getReturnValue();

Expand Down
13 changes: 12 additions & 1 deletion clang/lib/StaticAnalyzer/Core/BugReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2436,8 +2436,19 @@ PathSensitiveBugReport::getLocation() const {
if (auto FE = P.getAs<FunctionExitPoint>()) {
if (const ReturnStmt *RS = FE->getStmt())
return PathDiagnosticLocation::createBegin(RS, SM, LC);

// If we are exiting a destructor call, it is more useful to point to the
// next stmt which is usually the temporary declaration.
// For non-destructor and non-top-level calls, the next stmt will still
// refer to the last executed stmt of the body.
S = ErrorNode->getNextStmtForDiagnostics();
// If next stmt is not found, it is likely the end of a top-level function
// analysis. find the last execution statement then.
if (!S)
S = ErrorNode->getPreviousStmtForDiagnostics();
}
S = ErrorNode->getNextStmtForDiagnostics();
if (!S)
S = ErrorNode->getNextStmtForDiagnostics();
}

if (S) {
Expand Down
12 changes: 1 addition & 11 deletions clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Expand Down Expand Up @@ -923,17 +924,6 @@ SVal AnyCXXConstructorCall::getCXXThisVal() const {
return UnknownVal();
}

static bool isWithinStdNamespace(const Decl *D) {
const DeclContext *DC = D->getDeclContext();
while (DC) {
if (const auto *NS = dyn_cast<NamespaceDecl>(DC);
NS && NS->isStdNamespace())
return true;
DC = DC->getParent();
}
return false;
}

void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
SVal V = getCXXThisVal();
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,16 @@ std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State) {
return std::nullopt;
}

bool isWithinStdNamespace(const Decl *D) {
const DeclContext *DC = D->getDeclContext();
while (DC) {
if (const auto *NS = dyn_cast<NamespaceDecl>(DC);
NS && NS->isStdNamespace())
return true;
DC = DC->getParent();
}
return false;
}

} // namespace ento
} // namespace clang
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {

const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
if (const Stmt *S = N->getStmtForDiagnostics())
if (const Stmt *S = N->getStmtForDiagnostics(); S && !isa<CompoundStmt>(S))
return S;

return nullptr;
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1072,8 +1072,6 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
CleanedState, SFC, SymReaper);

// Process any special transfer function for dead symbols.
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node");
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
Expand Down Expand Up @@ -1102,10 +1100,15 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K);
Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, cleanupNodeTag(), K);
}
}

const ProgramPointTag *ExprEngine::cleanupNodeTag() {
static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node");
return &cleanupTag;
}

void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
Expand Down
9 changes: 9 additions & 0 deletions clang/test/AST/ByteCode/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,15 @@ namespace Incomplete {
constexpr int C = *F.a; // both-error {{must be initialized by a constant expression}} \
// both-note {{array-to-pointer decay of array member without known bound}}

struct X {
int a;
int b[];
};
extern X x;
constexpr int *xb = x.b; // both-error {{must be initialized by a constant expression}} \
// both-note {{array-to-pointer decay of array member without known bound}}


/// These are from test/SemaCXX/constant-expression-cxx11.cpp
extern int arr[];
constexpr int *c = &arr[1]; // both-error {{must be initialized by a constant expression}} \
Expand Down
3 changes: 3 additions & 0 deletions clang/test/AST/ByteCode/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ constexpr _Complex float getComplexFloat() {
static_assert(__real(getComplexFloat()) == 1, "");
static_assert(__imag(getComplexFloat()) == 2, "");

constexpr auto GH55390 = 1 / 65536j; // both-note {{division by zero}} \
// both-error {{constexpr variable 'GH55390' must be initialized by a constant expression}}

namespace CastToBool {
constexpr _Complex int F = {0, 1};
static_assert(F, "");
Expand Down
90 changes: 90 additions & 0 deletions clang/test/AST/ByteCode/constexpr-vectors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++14 -fsyntax-only -verify
// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter -std=c++14 -fsyntax-only -verify

using FourCharsVecSize __attribute__((vector_size(4))) = char;
using FourIntsVecSize __attribute__((vector_size(16))) = int;
using FourLongLongsVecSize __attribute__((vector_size(32))) = long long;
using FourFloatsVecSize __attribute__((vector_size(16))) = float;
using FourDoublesVecSize __attribute__((vector_size(32))) = double;
using FourI128VecSize __attribute__((vector_size(64))) = __int128;

using FourCharsExtVec __attribute__((ext_vector_type(4))) = char;
using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;
using FourI128ExtVec __attribute__((ext_vector_type(4))) = __int128;

// Only int vs float makes a difference here, so we only need to test 1 of each.
// Test Char to make sure the mixed-nature of shifts around char is evident.
void CharUsage() {
constexpr auto H = FourCharsVecSize{-1, -1, 0, -1};
constexpr auto InvH = -H;
static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, "");

constexpr auto ae = ~FourCharsVecSize{1, 2, 10, 20};
static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, "");

constexpr auto af = !FourCharsVecSize{0, 1, 8, -1};
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
}

void CharExtVecUsage() {
constexpr auto H = FourCharsExtVec{-1, -1, 0, -1};
constexpr auto InvH = -H;
static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, "");

constexpr auto ae = ~FourCharsExtVec{1, 2, 10, 20};
static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, "");

constexpr auto af = !FourCharsExtVec{0, 1, 8, -1};
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
}

void FloatUsage() {
constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00};
constexpr auto Z = -Y;
static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, "");

// Operator ~ is illegal on floats.
constexpr auto ae = ~FourFloatsVecSize{0, 1, 8, -1}; // expected-error {{invalid argument type}}

constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1};
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
}

void FloatVecUsage() {
constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00};
constexpr auto Z = -Y;
static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, "");

// Operator ~ is illegal on floats.
constexpr auto ae = ~FourFloatsVecSize{0, 1, 8, -1}; // expected-error {{invalid argument type}}

constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1};
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
}

void I128Usage() {
// Operator ~ is illegal on floats, so no test for that.
constexpr auto c = ~FourI128VecSize{1, 2, 10, 20};
static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, "");

constexpr auto d = !FourI128VecSize{0, 1, 8, -1};
static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, "");
}

void I128VecUsage() {
// Operator ~ is illegal on floats, so no test for that.
constexpr auto c = ~FourI128ExtVec{1, 2, 10, 20};
static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, "");

constexpr auto d = !FourI128ExtVec{0, 1, 8, -1};
static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, "");
}

using FourBoolsExtVec __attribute__((ext_vector_type(4))) = bool;
void BoolVecUsage() {
constexpr auto j = !FourBoolsExtVec{true, false, true, false};
static_assert(j[0] == false && j[1] == true && j[2] == false && j[3] == true, "");

constexpr auto k = ~FourBoolsExtVec{true, false, true, false};
static_assert(k[0] == false && k[1] == true && k[2] == false && k[3] == true, "");
}
13 changes: 13 additions & 0 deletions clang/test/AST/ByteCode/cxx11-pedantic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++11 -triple x86_64-linux -pedantic %s
// RUN: %clang_cc1 -verify=both,ref -std=c++11 -triple x86_64-linux -pedantic %s

struct T { int n; };
const T t = { 42 }; // both-note 2{{declared here}}
struct S {
int m : t.n; // both-warning {{width of bit-field 'm' (42 bits)}} \
// both-warning {{expression is not an integral constant expression}} \
// both-note {{read of non-constexpr variable 't' is not allowed}}
};

static_assert(t.n == 42, ""); // both-error {{expression is not an integral constant expression}} \
// both-note {{read of non-constexpr variable 't' is not allowed}}
7 changes: 7 additions & 0 deletions clang/test/AST/ByteCode/new-delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,13 @@ namespace CastedDelete {
// expected-note {{in call to}}
}

constexpr void use_after_free_2() { // both-error {{never produces a constant expression}}
struct X { constexpr void f() {} };
X *p = new X;
delete p;
p->f(); // both-note {{member call on heap allocated object that has been deleted}}
}

#else
/// Make sure we reject this prior to C++20
constexpr int a() { // both-error {{never produces a constant expression}}
Expand Down
20 changes: 8 additions & 12 deletions clang/test/AST/ByteCode/unions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,15 @@ namespace DefaultInit {

#if __cplusplus >= 202002L
namespace SimpleActivate {
constexpr int foo() { // ref-error {{never produces a constant expression}}
constexpr int foo() { // both-error {{never produces a constant expression}}
union {
int a;
int b;
} Z;

Z.a = 10;
Z.b = 20;
return Z.a; // both-note {{read of member 'a' of union with active member 'b'}} \
// ref-note {{read of member 'a' of union with active member 'b}}
return Z.a; // both-note 2{{read of member 'a' of union with active member 'b'}}
}
static_assert(foo() == 20); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
Expand Down Expand Up @@ -212,11 +211,10 @@ namespace Nested {
int y;
};

constexpr int foo() { // ref-error {{constexpr function never produces a constant expression}}
constexpr int foo() { // both-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.y; // both-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}} \
// ref-note {{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}
int a = u.y; // both-note 2{{read of member 'y' of union with active member 'u' is not allowed in a constant expression}}

return 1;
}
Expand All @@ -230,24 +228,22 @@ namespace Nested {
}
static_assert(foo2() == 10);

constexpr int foo3() { // ref-error {{constexpr function never produces a constant expression}}
constexpr int foo3() { // both-error {{constexpr function never produces a constant expression}}
U2 u;
u.u.a = 10;
int a = u.u.b; // both-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}} \
// ref-note {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
int a = u.u.b; // both-note 2{{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}

return 1;
}
static_assert(foo3() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

constexpr int foo4() { // ref-error {{constexpr function never produces a constant expression}}
constexpr int foo4() { // both-error {{constexpr function never produces a constant expression}}
U2 u;

u.x = 10;

return u.u.a;// both-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}} \
// ref-note {{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
return u.u.a; // both-note 2{{read of member 'u' of union with active member 'x' is not allowed in a constant expression}}
}
static_assert(foo4() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
Expand Down
165 changes: 165 additions & 0 deletions clang/test/Analysis/block-in-critical-section-inheritance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,168 @@ void gh_99628() {
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
}

void no_false_positive_gh_104241() {
std::mutex m;
m.lock();
// If inheritance not handled properly, this unlock might not match the lock
// above because technically they act on different memory regions:
// __mutex_base and mutex.
m.unlock();
sleep(10); // no-warning
}

struct TwoMutexes {
std::mutex m1;
std::mutex m2;
};

void two_mutexes_no_false_negative(TwoMutexes &tm) {
tm.m1.lock();
// expected-note@-1 {{Entering critical section here}}
tm.m2.unlock();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
tm.m1.unlock();
}

struct MyMutexBase1 : std::mutex {
void lock1() { lock(); }
// expected-note@-1 {{Entering critical section here}}
void unlock1() { unlock(); }
};
struct MyMutexBase2 : std::mutex {
void lock2() { lock(); }
void unlock2() { unlock(); }
};
struct MyMutex : MyMutexBase1, MyMutexBase2 {};
// MyMutex has two distinct std::mutex as base classes

void custom_mutex_tp(MyMutexBase1 &mb) {
mb.lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
mb.unlock();
}

void custom_mutex_tn(MyMutexBase1 &mb) {
mb.lock();
mb.unlock();
sleep(10);
}

void custom_mutex_cast_tp(MyMutexBase1 &mb) {
static_cast<std::mutex&>(mb).lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
static_cast<std::mutex&>(mb).unlock();
}

void custom_mutex_cast_tn(MyMutexBase1 &mb) {
static_cast<std::mutex&>(mb).lock();
static_cast<std::mutex&>(mb).unlock();
sleep(10);
}

void two_custom_mutex_bases_tp(MyMutex &m) {
m.lock1();
// expected-note@-1 {{Calling 'MyMutexBase1::lock1'}}
// expected-note@-2 {{Returning from 'MyMutexBase1::lock1'}}
m.unlock2();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock1();
}

void two_custom_mutex_bases_tn(MyMutex &m) {
m.lock1();
m.unlock1();
sleep(10);
}

void two_custom_mutex_bases_casts_tp(MyMutex &m) {
static_cast<MyMutexBase1&>(m).lock();
// expected-note@-1 {{Entering critical section here}}
static_cast<MyMutexBase2&>(m).unlock();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
static_cast<MyMutexBase1&>(m).unlock();
}

void two_custom_mutex_bases_casts_tn(MyMutex &m) {
static_cast<MyMutexBase1&>(m).lock();
static_cast<MyMutexBase1&>(m).unlock();
sleep(10);
}

struct MutexVirtBase1 : virtual std::mutex {
void lock1() { lock(); }
// expected-note@-1 {{Entering critical section here}}
void unlock1() { unlock(); }
};

struct MutexVirtBase2 : virtual std::mutex {
void lock2() { lock(); }
void unlock2() { unlock(); }
};

struct CombinedVirtMutex : MutexVirtBase1, MutexVirtBase2 {};

void virt_inherited_mutexes_same_base_tn1(CombinedVirtMutex &cvt) {
cvt.lock1();
cvt.unlock1();
sleep(10);
}

void virt_inherited_mutexes_different_bases_tn(CombinedVirtMutex &cvt) {
cvt.lock1();
cvt.unlock2(); // Despite a different name, unlock2 acts on the same mutex as lock1
sleep(10);
}

void virt_inherited_mutexes_different_bases_tp(CombinedVirtMutex &cvt) {
cvt.lock1();
// expected-note@-1 {{Calling 'MutexVirtBase1::lock1'}}
// expected-note@-2 {{Returning from 'MutexVirtBase1::lock1'}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
cvt.unlock1();
}

namespace std {
template <class... MutexTypes> struct scoped_lock {
explicit scoped_lock(MutexTypes&... m);
~scoped_lock();
};
template <class MutexType> class scoped_lock<MutexType> {
public:
explicit scoped_lock(MutexType& m) : m(m) { m.lock(); }
~scoped_lock() { m.unlock(); }
private:
MutexType& m;
};
} // namespace std

namespace gh_104241 {
int magic_number;
std::mutex m;

void fixed() {
int current;
for (int items_processed = 0; items_processed < 100; ++items_processed) {
{
std::scoped_lock<std::mutex> guard(m);
current = magic_number;
}
sleep(current); // expected no warning
}
}
} // namespace gh_104241
42 changes: 42 additions & 0 deletions clang/test/Analysis/block-in-critical-section-nested-namespace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=unix.BlockInCriticalSection \
// RUN: -std=c++11 \
// RUN: -analyzer-output text \
// RUN: -verify %s

unsigned int sleep(unsigned int seconds) {return 0;}
namespace std {
namespace __detail {
class __mutex_base {
public:
void lock();
};
} // namespace __detail

class mutex : public __detail::__mutex_base{
public:
void unlock();
bool try_lock();
};
} // namespace std

void gh_99628() {
std::mutex m;
m.lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
}

void no_false_positive_gh_104241() {
std::mutex m;
m.lock();
// If inheritance not handled properly, this unlock might not match the lock
// above because technically they act on different memory regions:
// __mutex_base and mutex.
m.unlock();
sleep(10); // no-warning
}
20 changes: 10 additions & 10 deletions clang/test/Analysis/copy-elision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,19 @@ ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
return ClassWithoutDestructor(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
return make1(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
return make2(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}

void testMultipleReturns() {
Expand All @@ -193,7 +193,7 @@ void testMultipleReturns() {
void consume(ClassWithoutDestructor c) {
c.push();
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'c' is still referred to by the stack variable 'v' upon returning \
variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}

Expand Down Expand Up @@ -267,7 +267,7 @@ struct TestCtorInitializer {
: c(ClassWithDestructor(v)) {}
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
};

void testCtorInitializer() {
Expand Down Expand Up @@ -303,19 +303,19 @@ ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
return ClassWithDestructor(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
return make1(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
return make2(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}

void testMultipleReturnsWithDestructors() {
Expand Down Expand Up @@ -360,7 +360,7 @@ void testMultipleReturnsWithDestructors() {
void consume(ClassWithDestructor c) {
c.push();
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'c' is still referred to by the stack variable 'v' upon returning \
variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}

Expand Down Expand Up @@ -407,7 +407,7 @@ struct Foo {
Foo make1(Foo **r) {
return Foo(r);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'Foo' is still referred to by the stack \
object of type 'Foo' is still referred to by the caller \
variable 'z' upon returning to the caller}}
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/Analysis/incorrect-checker-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ char const *p;
void f0() {
char const str[] = "This will change";
p = str;
} // expected-warning{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
// expected-note@-1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
} // expected-warning@-1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
// expected-note@-2{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
2 changes: 1 addition & 1 deletion clang/test/Analysis/loop-block-counts.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void callee(void **p) {
int x;
*p = &x;
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'x' is still referred to by the stack variable 'arr' upon \
variable 'x' is still referred to by the caller variable 'arr' upon \
returning to the caller}}
}

Expand Down
6 changes: 3 additions & 3 deletions clang/test/Analysis/stack-addr-ps.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ void callTestRegister(void) {

void top_level_leaking(int **out) {
int local = 42;
*out = &local; // no-warning FIXME
*out = &local; // expected-warning{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'out'}}
}

void callee_leaking_via_param(int **out) {
int local = 1;
*out = &local;
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}

void caller_for_leaking_callee() {
Expand All @@ -115,7 +115,7 @@ void caller_for_leaking_callee() {
void callee_nested_leaking(int **out) {
int local = 1;
*out = &local;
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}

void caller_mid_for_nested_leaking(int **mid) {
Expand Down
Loading