179 changes: 47 additions & 132 deletions clang/lib/AST/DeclPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;

Expand Down Expand Up @@ -49,18 +50,6 @@ namespace {

void PrintObjCTypeParams(ObjCTypeParamList *Params);

enum class AttrPrintLoc {
None = 0,
Left = 1,
Right = 2,
Any = Left | Right,

LLVM_MARK_AS_BITMASK_ENUM(/*DefaultValue=*/Any)
};

void prettyPrintAttributes(Decl *D, raw_ostream &out,
AttrPrintLoc loc = AttrPrintLoc::Any);

public:
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
const ASTContext &Context, unsigned Indentation = 0,
Expand Down Expand Up @@ -129,11 +118,10 @@ namespace {
const TemplateParameterList *Params);
void printTemplateArguments(llvm::ArrayRef<TemplateArgumentLoc> Args,
const TemplateParameterList *Params);

inline void prettyPrintAttributes(Decl *D) {
prettyPrintAttributes(D, Out);
}

enum class AttrPosAsWritten { Default = 0, Left, Right };
void
prettyPrintAttributes(const Decl *D,
AttrPosAsWritten Pos = AttrPosAsWritten::Default);
void prettyPrintPragmas(Decl *D);
void printDeclType(QualType T, StringRef DeclName, bool Pack = false);
};
Expand Down Expand Up @@ -250,87 +238,48 @@ raw_ostream& DeclPrinter::Indent(unsigned Indentation) {
return Out;
}

// For CLANG_ATTR_LIST_CanPrintOnLeft macro.
#include "clang/Basic/AttrLeftSideCanPrintList.inc"
static DeclPrinter::AttrPosAsWritten getPosAsWritten(const Attr *A,
const Decl *D) {
SourceLocation ALoc = A->getLoc();
SourceLocation DLoc = D->getLocation();
const ASTContext &C = D->getASTContext();
if (ALoc.isInvalid() || DLoc.isInvalid())
return DeclPrinter::AttrPosAsWritten::Left;

// For CLANG_ATTR_LIST_PrintOnLeft macro.
#include "clang/Basic/AttrLeftSideMustPrintList.inc"
if (C.getSourceManager().isBeforeInTranslationUnit(ALoc, DLoc))
return DeclPrinter::AttrPosAsWritten::Left;

static bool canPrintOnLeftSide(attr::Kind kind) {
#ifdef CLANG_ATTR_LIST_CanPrintOnLeft
switch (kind) {
CLANG_ATTR_LIST_CanPrintOnLeft
return true;
default:
return false;
}
#else
return false;
#endif
}

static bool canPrintOnLeftSide(const Attr *A) {
if (A->isStandardAttributeSyntax())
return false;

return canPrintOnLeftSide(A->getKind());
return DeclPrinter::AttrPosAsWritten::Right;
}

static bool mustPrintOnLeftSide(attr::Kind kind) {
#ifdef CLANG_ATTR_LIST_PrintOnLeft
switch (kind) {
CLANG_ATTR_LIST_PrintOnLeft
return true;
default:
return false;
}
#else
return false;
#endif
}

static bool mustPrintOnLeftSide(const Attr *A) {
if (A->isDeclspecAttribute())
return true;

return mustPrintOnLeftSide(A->getKind());
}

void DeclPrinter::prettyPrintAttributes(Decl *D, llvm::raw_ostream &Out,
AttrPrintLoc Loc) {
void DeclPrinter::prettyPrintAttributes(const Decl *D,
AttrPosAsWritten Pos /*=Default*/) {
if (Policy.PolishForDeclaration)
return;

if (D->hasAttrs()) {
AttrVec &Attrs = D->getAttrs();
const AttrVec &Attrs = D->getAttrs();
for (auto *A : Attrs) {
if (A->isInherited() || A->isImplicit())
continue;

AttrPrintLoc AttrLoc = AttrPrintLoc::Right;
if (mustPrintOnLeftSide(A)) {
// If we must always print on left side (e.g. declspec), then mark as
// so.
AttrLoc = AttrPrintLoc::Left;
} else if (canPrintOnLeftSide(A)) {
// For functions with body defined we print the attributes on the left
// side so that GCC accept our dumps as well.
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
FD && FD->isThisDeclarationADefinition())
// In case Decl is a function with a body, then attrs should be print
// on the left side.
AttrLoc = AttrPrintLoc::Left;

// In case it is a variable declaration with a ctor, then allow
// printing on the left side for readbility.
else if (const VarDecl *VD = dyn_cast<VarDecl>(D);
VD && VD->getInit() &&
VD->getInitStyle() == VarDecl::CallInit)
AttrLoc = AttrPrintLoc::Left;
switch (A->getKind()) {
#define ATTR(X)
#define PRAGMA_SPELLING_ATTR(X) case attr::X:
#include "clang/Basic/AttrList.inc"
break;
default:
AttrPosAsWritten APos = getPosAsWritten(A, D);
assert(APos != AttrPosAsWritten::Default &&
"Default not a valid for an attribute location");
if (Pos == AttrPosAsWritten::Default || Pos == APos) {
if (Pos != AttrPosAsWritten::Left)
Out << ' ';
A->printPretty(Out, Policy);
if (Pos == AttrPosAsWritten::Left)
Out << ' ';
}
break;
}
// Only print the side matches the user requested.
if ((Loc & AttrLoc) != AttrPrintLoc::None)
A->printPretty(Out, Policy);
}
}
}
Expand Down Expand Up @@ -691,8 +640,10 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,

void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (!D->getDescribedFunctionTemplate() &&
!D->isFunctionTemplateSpecialization())
!D->isFunctionTemplateSpecialization()) {
prettyPrintPragmas(D);
prettyPrintAttributes(D, AttrPosAsWritten::Left);
}

if (D->isFunctionTemplateSpecialization())
Out << "template<> ";
Expand All @@ -702,22 +653,6 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
printTemplateParameters(D->getTemplateParameterList(I));
}

std::string LeftsideAttrs;
llvm::raw_string_ostream LSAS(LeftsideAttrs);

prettyPrintAttributes(D, LSAS, AttrPrintLoc::Left);

// prettyPrintAttributes print a space on left side of the attribute.
if (LeftsideAttrs[0] == ' ') {
// Skip the space prettyPrintAttributes generated.
LeftsideAttrs.erase(0, LeftsideAttrs.find_first_not_of(' '));

// Add a single space between the attribute and the Decl name.
LSAS << ' ';
}

Out << LeftsideAttrs;

CXXConstructorDecl *CDecl = dyn_cast<CXXConstructorDecl>(D);
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
Expand Down Expand Up @@ -883,7 +818,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
Ty.print(Out, Policy, Proto);
}

prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
prettyPrintAttributes(D, AttrPosAsWritten::Right);

if (D->isPureVirtual())
Out << " = 0";
Expand Down Expand Up @@ -976,27 +911,12 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
void DeclPrinter::VisitVarDecl(VarDecl *D) {
prettyPrintPragmas(D);

prettyPrintAttributes(D, AttrPosAsWritten::Left);

if (const auto *Param = dyn_cast<ParmVarDecl>(D);
Param && Param->isExplicitObjectParameter())
Out << "this ";

std::string LeftSide;
llvm::raw_string_ostream LeftSideStream(LeftSide);

// Print attributes that should be placed on the left, such as __declspec.
prettyPrintAttributes(D, LeftSideStream, AttrPrintLoc::Left);

// prettyPrintAttributes print a space on left side of the attribute.
if (LeftSide[0] == ' ') {
// Skip the space prettyPrintAttributes generated.
LeftSide.erase(0, LeftSide.find_first_not_of(' '));

// Add a single space between the attribute and the Decl name.
LeftSideStream << ' ';
}

Out << LeftSide;

QualType T = D->getTypeSourceInfo()
? D->getTypeSourceInfo()->getType()
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
Expand Down Expand Up @@ -1029,21 +949,16 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
}
}

StringRef Name;

Name = (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
D->getIdentifier())
? D->getIdentifier()->deuglifiedName()
: D->getName();

if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
!Policy.SuppressUnwrittenScope)
MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
printDeclType(T, Name);

// Print the attributes that should be placed right before the end of the
// decl.
prettyPrintAttributes(D, Out, AttrPrintLoc::Right);
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
D->getIdentifier())
? D->getIdentifier()->deuglifiedName()
: D->getName());

prettyPrintAttributes(D, AttrPosAsWritten::Right);

Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,9 @@ static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx,
if (const auto *Ref = T->getAs<ReferenceType>())
T = Ref->getPointeeType();

if (T.getQualifiers().hasUnaligned())
return CharUnits::One();

// __alignof is defined to return the preferred alignment.
// Before 8, clang returned the preferred alignment for alignof and
// _Alignof as well.
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,34 @@ LLVM_DUMP_METHOD void InterpFrame::dump(llvm::raw_ostream &OS,
F = F->Caller;
}
}

LLVM_DUMP_METHOD void Record::dump(llvm::raw_ostream &OS, unsigned Indentation,
unsigned Offset) const {
unsigned Indent = Indentation * 2;
OS.indent(Indent);
{
ColorScope SC(OS, true, {llvm::raw_ostream::BLUE, true});
OS << getName() << "\n";
}

unsigned I = 0;
for (const Record::Base &B : bases()) {
OS.indent(Indent) << "- Base " << I << ". Offset " << (Offset + B.Offset)
<< "\n";
B.R->dump(OS, Indentation + 1, Offset + B.Offset);
++I;
}

// FIXME: Virtual bases.

I = 0;
for (const Record::Field &F : fields()) {
OS.indent(Indent) << "- Field " << I << ": ";
{
ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_RED, true});
OS << F.Decl->getName();
}
OS << ". Offset " << (Offset + F.Offset) << "\n";
++I;
}
}
26 changes: 26 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,18 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return true;
}

for (const auto &P : {LHS, RHS}) {
if (P.isZero())
continue;
if (const ValueDecl *VD = P.getDeclDesc()->asValueDecl();
VD && VD->isWeak()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_pointer_weak_comparison)
<< P.toDiagnosticString(S.getCtx());
return false;
}
}

if (!Pointer::hasSameBase(LHS, RHS)) {
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
return true;
Expand Down Expand Up @@ -1333,6 +1345,11 @@ inline bool FinishInit(InterpState &S, CodePtr OpPC) {
return true;
}

inline bool Dump(InterpState &S, CodePtr OpPC) {
S.Stk.dump();
return true;
}

inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
const Pointer &Ptr) {
Pointer Base = Ptr;
Expand Down Expand Up @@ -1841,6 +1858,15 @@ inline bool This(InterpState &S, CodePtr OpPC) {
if (!CheckThis(S, OpPC, This))
return false;

// Ensure the This pointer has been cast to the correct base.
if (!This.isDummy()) {
assert(isa<CXXMethodDecl>(S.Current->getFunction()->getDecl()));
assert(This.getRecord());
assert(
This.getRecord()->getDecl() ==
cast<CXXMethodDecl>(S.Current->getFunction()->getDecl())->getParent());
}

S.Stk.push<Pointer>(This);
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -723,3 +723,8 @@ def CheckNonNullArg : Opcode {
}

def Memcpy : Opcode;

//===----------------------------------------------------------------------===//
// Debugging.
//===----------------------------------------------------------------------===//
def Dump : Opcode;
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ void Pointer::initialize() const {
if (isStatic() && Base == 0)
return;

// Nothing to do for these.
if (Desc->getNumElems() == 0)
return;

InitMapPtr &IM = getInitMap();
if (!IM)
IM =
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/Interp/Record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "Record.h"
#include "clang/AST/ASTContext.h"

using namespace clang;
using namespace clang::interp;
Expand All @@ -27,6 +28,14 @@ Record::Record(const RecordDecl *Decl, BaseList &&SrcBases,
VirtualBaseMap[V.Decl] = &V;
}

const std::string Record::getName() const {
std::string Ret;
llvm::raw_string_ostream OS(Ret);
Decl->getNameForDiagnostic(OS, Decl->getASTContext().getPrintingPolicy(),
/*Qualified=*/true);
return Ret;
}

const Record::Field *Record::getField(const FieldDecl *FD) const {
auto It = FieldMap.find(FD);
assert(It != FieldMap.end() && "Missing field");
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/AST/Interp/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Record final {
/// Returns the underlying declaration.
const RecordDecl *getDecl() const { return Decl; }
/// Returns the name of the underlying declaration.
const std::string getName() const { return Decl->getNameAsString(); }
const std::string getName() const;
/// Checks if the record is a union.
bool isUnion() const { return getDecl()->isUnion(); }
/// Returns the size of the record.
Expand Down Expand Up @@ -100,6 +100,10 @@ class Record final {
unsigned getNumVirtualBases() const { return VirtualBases.size(); }
const Base *getVirtualBase(unsigned I) const { return &VirtualBases[I]; }

void dump(llvm::raw_ostream &OS, unsigned Indentation = 0,
unsigned Offset = 0) const;
void dump() const { dump(llvm::errs()); }

private:
/// Constructor used by Program to create record descriptors.
Record(const RecordDecl *, BaseList &&Bases, FieldList &&Fields,
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,11 @@ void StmtPrinter::VisitLabelStmt(LabelStmt *Node) {
}

void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) {
for (const auto *Attr : Node->getAttrs()) {
llvm::ArrayRef<const Attr *> Attrs = Node->getAttrs();
for (const auto *Attr : Attrs) {
Attr->printPretty(OS, Policy);
if (Attr != Attrs.back())
OS << ' ';
}

PrintStmt(Node->getSubStmt(), 0);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,7 @@ void StmtProfiler::VisitMSPropertySubscriptExpr(
void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isImplicit());
ID.AddBoolean(S->isCapturedByCopyInLambdaWithExplicitObjectParameter());
}

void StmtProfiler::VisitCXXThrowExpr(const CXXThrowExpr *S) {
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/AST/TextNodeDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,8 +1194,11 @@ void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {
case NOUR_Constant: OS << " non_odr_use_constant"; break;
case NOUR_Discarded: OS << " non_odr_use_discarded"; break;
}
if (Node->refersToEnclosingVariableOrCapture())
if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
OS << " dependent_capture";
else if (Node->refersToEnclosingVariableOrCapture())
OS << " refers_to_enclosing_variable_or_capture";

if (Node->isImmediateEscalating())
OS << " immediate-escalating";
}
Expand Down Expand Up @@ -1351,6 +1354,8 @@ void TextNodeDumper::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *Node) {
void TextNodeDumper::VisitCXXThisExpr(const CXXThisExpr *Node) {
if (Node->isImplicit())
OS << " implicit";
if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
OS << " dependent_capture";
OS << " this";
}

Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17288,6 +17288,16 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
Value *Op1 = EmitScalarExpr(E->getArg(1));
Value *Op2 = EmitScalarExpr(E->getArg(2));
Value *Op3 = EmitScalarExpr(E->getArg(3));
// rldimi is 64-bit instruction, expand the intrinsic before isel to
// leverage peephole and avoid legalization efforts.
if (BuiltinID == PPC::BI__builtin_ppc_rldimi &&
!getTarget().getTriple().isPPC64()) {
Function *F = CGM.getIntrinsic(Intrinsic::fshl, Op0->getType());
Op2 = Builder.CreateZExt(Op2, Int64Ty);
Value *Shift = Builder.CreateCall(F, {Op0, Op0, Op2});
return Builder.CreateOr(Builder.CreateAnd(Shift, Op3),
Builder.CreateAnd(Op1, Builder.CreateNot(Op3)));
}
return Builder.CreateCall(
CGM.getIntrinsic(BuiltinID == PPC::BI__builtin_ppc_rldimi
? Intrinsic::ppc_rldimi
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "BuildingBuiltinDumpStructCall";
case CodeSynthesisContext::BuildingDeductionGuides:
return "BuildingDeductionGuides";
case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation:
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
return "TypeAliasTemplateInstantiation";
}
return "";
Expand Down
5 changes: 1 addition & 4 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,10 +720,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
if (LangOpts.CPlusPlus20) {
Builder.defineMacro("__cpp_aggregate_paren_init", "201902L");

// P0848 is implemented, but we're still waiting for other concepts
// issues to be addressed before bumping __cpp_concepts up to 202002L.
// Refer to the discussion of this at https://reviews.llvm.org/D128619.
Builder.defineMacro("__cpp_concepts", "201907L");
Builder.defineMacro("__cpp_concepts", "202002");
Builder.defineMacro("__cpp_conditional_explicit", "201806L");
Builder.defineMacro("__cpp_consteval", "202211L");
Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L");
Expand Down
1 change: 0 additions & 1 deletion clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5232,7 +5232,6 @@ static bool isPPC_64Builtin(unsigned BuiltinID) {
case PPC::BI__builtin_ppc_fetch_and_andlp:
case PPC::BI__builtin_ppc_fetch_and_orlp:
case PPC::BI__builtin_ppc_fetch_and_swaplp:
case PPC::BI__builtin_ppc_rldimi:
return true;
}
return false;
Expand Down
55 changes: 40 additions & 15 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3442,10 +3442,11 @@ static bool ShouldLookupResultBeMultiVersionOverload(const LookupResult &R) {

ExprResult Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
LookupResult &R, bool NeedsADL,
bool AcceptInvalidDecl) {
bool AcceptInvalidDecl,
bool NeedUnresolved) {
// If this is a single, fully-resolved result and we don't need ADL,
// just build an ordinary singleton decl ref.
if (!NeedsADL && R.isSingleResult() &&
if (!NeedUnresolved && !NeedsADL && R.isSingleResult() &&
!R.getAsSingle<FunctionTemplateDecl>() &&
!ShouldLookupResultBeMultiVersionOverload(R))
return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), R.getFoundDecl(),
Expand Down Expand Up @@ -18981,8 +18982,10 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
// Note that we skip the implicit instantiation of templates that are only
// used in unused default arguments or by recursive calls to themselves.
// This is formally non-conforming, but seems reasonable in practice.
bool NeedDefinition = !IsRecursiveCall && (OdrUse == OdrUseContext::Used ||
NeededForConstantEvaluation);
bool NeedDefinition =
!IsRecursiveCall &&
(OdrUse == OdrUseContext::Used ||
(NeededForConstantEvaluation && !Func->isPureVirtual()));

// C++14 [temp.expl.spec]p6:
// If a template [...] is explicitly specialized then that specialization
Expand Down Expand Up @@ -20719,20 +20722,42 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) {
static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(
Sema &SemaRef, ValueDecl *D, Expr *E) {
auto *ID = dyn_cast<DeclRefExpr>(E);
if (!ID || ID->isTypeDependent())
if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture())
return;

// If any enclosing lambda with a dependent explicit object parameter either
// explicitly captures the variable by value, or has a capture default of '='
// and does not capture the variable by reference, then the type of the DRE
// is dependent on the type of that lambda's explicit object parameter.
auto IsDependent = [&]() {
const LambdaScopeInfo *LSI = SemaRef.getCurLambda();
if (!LSI)
return false;
if (!LSI->ExplicitObjectParameter ||
!LSI->ExplicitObjectParameter->getType()->isDependentType())
return false;
if (!LSI->CaptureMap.count(D))
return false;
const Capture &Cap = LSI->getCapture(D);
return !Cap.isCopyCapture();
for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) {
auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope);
if (!LSI)
continue;

if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) &&
LSI->AfterParameterList)
return false;

const auto *MD = LSI->CallOperator;
if (MD->getType().isNull())
continue;

const auto *Ty = cast<FunctionProtoType>(MD->getType());
if (!Ty || !MD->isExplicitObjectMemberFunction() ||
!Ty->getParamType(0)->isDependentType())
continue;

if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) {
if (C->isCopyCapture())
return true;
continue;
}

if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval)
return true;
}
return false;
}();

ID->setCapturedByCopyInLambdaWithExplicitObjectParameter(
Expand Down
99 changes: 77 additions & 22 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1415,26 +1415,42 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
}

ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
// C++20 [expr.prim.this]p1:
// The keyword this names a pointer to the object for which an
// implicit object member function is invoked or a non-static
// data member's initializer is evaluated.
QualType ThisTy = getCurrentThisType();

if (ThisTy.isNull()) {
DeclContext *DC = getFunctionLevelDeclContext();
if (CheckCXXThisType(Loc, ThisTy))
return ExprError();

if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
Method && Method->isExplicitObjectMemberFunction()) {
return Diag(Loc, diag::err_invalid_this_use) << 1;
}
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}

if (isLambdaCallWithExplicitObjectParameter(CurContext))
return Diag(Loc, diag::err_invalid_this_use) << 1;
bool Sema::CheckCXXThisType(SourceLocation Loc, QualType Type) {
if (!Type.isNull())
return false;

return Diag(Loc, diag::err_invalid_this_use) << 0;
// C++20 [expr.prim.this]p3:
// If a declaration declares a member function or member function template
// of a class X, the expression this is a prvalue of type
// "pointer to cv-qualifier-seq X" wherever X is the current class between
// the optional cv-qualifier-seq and the end of the function-definition,
// member-declarator, or declarator. It shall not appear within the
// declaration of either a static member function or an explicit object
// member function of the current class (although its type and value
// category are defined within such member functions as they are within
// an implicit object member function).
DeclContext *DC = getFunctionLevelDeclContext();
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
Method && Method->isExplicitObjectMemberFunction()) {
Diag(Loc, diag::err_invalid_this_use) << 1;
} else if (isLambdaCallWithExplicitObjectParameter(CurContext)) {
Diag(Loc, diag::err_invalid_this_use) << 1;
} else {
Diag(Loc, diag::err_invalid_this_use) << 0;
}

return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
return true;
}

Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
Expand All @@ -1446,6 +1462,42 @@ Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,

void Sema::MarkThisReferenced(CXXThisExpr *This) {
CheckCXXThisCapture(This->getExprLoc());
if (This->isTypeDependent())
return;

// Check if 'this' is captured by value in a lambda with a dependent explicit
// object parameter, and mark it as type-dependent as well if so.
auto IsDependent = [&]() {
for (auto *Scope : llvm::reverse(FunctionScopes)) {
auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope);
if (!LSI)
continue;

if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
LSI->AfterParameterList)
return false;

// If this lambda captures 'this' by value, then 'this' is dependent iff
// this lambda has a dependent explicit object parameter. If we can't
// determine whether it does (e.g. because the CXXMethodDecl's type is
// null), assume it doesn't.
if (LSI->isCXXThisCaptured()) {
if (!LSI->getCXXThisCapture().isCopyCapture())
continue;

const auto *MD = LSI->CallOperator;
if (MD->getType().isNull())
return false;

const auto *Ty = cast<FunctionProtoType>(MD->getType());
return Ty && MD->isExplicitObjectMemberFunction() &&
Ty->getParamType(0)->isDependentType();
}
}
return false;
}();

This->setCapturedByCopyInLambdaWithExplicitObjectParameter(IsDependent);
}

bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
Expand Down Expand Up @@ -5566,8 +5618,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
}
}

static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
QualType RhsT, SourceLocation KeyLoc);
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc);

static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
Expand All @@ -5583,8 +5635,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
// Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
// alongside the IsConstructible traits to avoid duplication.
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary && Kind != BTT_ReferenceConstructsFromTemporary)
return EvaluateBinaryTypeTrait(S, Kind, Args[0]->getType(),
Args[1]->getType(), RParenLoc);
return EvaluateBinaryTypeTrait(S, Kind, Args[0],
Args[1], RParenLoc);

switch (Kind) {
case clang::BTT_ReferenceBindsToTemporary:
Expand Down Expand Up @@ -5679,8 +5731,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
if (U->isReferenceType())
return false;

QualType TPtr = S.Context.getPointerType(S.BuiltinRemoveReference(T, UnaryTransformType::RemoveCVRef, {}));
QualType UPtr = S.Context.getPointerType(S.BuiltinRemoveReference(U, UnaryTransformType::RemoveCVRef, {}));
TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(T, UnaryTransformType::RemoveCVRef, {})));
TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(U, UnaryTransformType::RemoveCVRef, {})));
return EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsConvertibleTo, UPtr, TPtr, RParenLoc);
}

Expand Down Expand Up @@ -5814,8 +5866,11 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
}

static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
QualType RhsT, SourceLocation KeyLoc) {
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();

assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
"Cannot evaluate traits of dependent types");

Expand Down
42 changes: 30 additions & 12 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ enum IMAKind {
/// The reference is a contextually-permitted abstract member reference.
IMA_Abstract,

/// Whether the context is static is dependent on the enclosing template (i.e.
/// in a dependent class scope explicit specialization).
IMA_Dependent,

/// The reference may be to an unresolved using declaration and the
/// context is not an instance method.
IMA_Unresolved_StaticOrExplicitContext,
Expand Down Expand Up @@ -91,10 +95,18 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,

DeclContext *DC = SemaRef.getFunctionLevelDeclContext();

bool isStaticOrExplicitContext =
SemaRef.CXXThisTypeOverride.isNull() &&
(!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic() ||
cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction());
bool couldInstantiateToStatic = false;
bool isStaticOrExplicitContext = SemaRef.CXXThisTypeOverride.isNull();

if (auto *MD = dyn_cast<CXXMethodDecl>(DC)) {
if (MD->isImplicitObjectMemberFunction()) {
isStaticOrExplicitContext = false;
// A dependent class scope function template explicit specialization
// that is neither declared 'static' nor with an explicit object
// parameter could instantiate to a static or non-static member function.
couldInstantiateToStatic = MD->getDependentSpecializationInfo();
}
}

if (R.isUnresolvableResult())
return isStaticOrExplicitContext ? IMA_Unresolved_StaticOrExplicitContext
Expand Down Expand Up @@ -123,6 +135,9 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
if (Classes.empty())
return IMA_Static;

if (couldInstantiateToStatic)
return IMA_Dependent;

// C++11 [expr.prim.general]p12:
// An id-expression that denotes a non-static data member or non-static
// member function of a class can only be used:
Expand Down Expand Up @@ -268,27 +283,30 @@ ExprResult Sema::BuildPossibleImplicitMemberExpr(
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
UnresolvedLookupExpr *AsULE) {
switch (ClassifyImplicitMemberAccess(*this, R)) {
switch (IMAKind Classification = ClassifyImplicitMemberAccess(*this, R)) {
case IMA_Instance:
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, true, S);

case IMA_Mixed:
case IMA_Mixed_Unrelated:
case IMA_Unresolved:
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, false,
S);

return BuildImplicitMemberExpr(
SS, TemplateKWLoc, R, TemplateArgs,
/*IsKnownInstance=*/Classification == IMA_Instance, S);
case IMA_Field_Uneval_Context:
Diag(R.getNameLoc(), diag::warn_cxx98_compat_non_static_member_use)
<< R.getLookupNameInfo().getName();
[[fallthrough]];
case IMA_Static:
case IMA_Abstract:
case IMA_Dependent:
case IMA_Mixed_StaticOrExplicitContext:
case IMA_Unresolved_StaticOrExplicitContext:
if (TemplateArgs || TemplateKWLoc.isValid())
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, false, TemplateArgs);
return AsULE ? AsULE : BuildDeclarationNameExpr(SS, R, false);
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*RequiresADL=*/false,
TemplateArgs);
return AsULE ? AsULE
: BuildDeclarationNameExpr(
SS, R, /*NeedsADL=*/false, /*AcceptInvalidDecl=*/false,
/*NeedUnresolved=*/Classification == IMA_Dependent);

case IMA_Error_StaticOrExplicitContext:
case IMA_Error_Unrelated:
Expand Down
13 changes: 5 additions & 8 deletions clang/lib/Sema/SemaOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,11 @@ bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K,

bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind,
OpenACCClauseKind ClauseKind) {
switch (ClauseKind) {
// FIXME: For each clause as we implement them, we can add the
// 'legalization' list here.
default:
// Do nothing so we can go to the 'unimplemented' diagnostic instead.
return true;
}
llvm_unreachable("Invalid clause kind");
// FIXME: For each clause as we implement them, we can add the
// 'legalization' list here.

// Do nothing so we can go to the 'unimplemented' diagnostic instead.
return true;
}
} // namespace

Expand Down
10 changes: 4 additions & 6 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4494,15 +4494,13 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
AliasTemplate->getTemplateParameters()->getDepth());

LocalInstantiationScope Scope(*this);
InstantiatingTemplate Inst(*this, TemplateLoc, Template);
InstantiatingTemplate Inst(
*this, /*PointOfInstantiation=*/TemplateLoc,
/*Entity=*/AliasTemplate,
/*TemplateArgs=*/TemplateArgLists.getInnermost());
if (Inst.isInvalid())
return QualType();

InstantiatingTemplate InstTemplate(
*this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(),
/*Template=*/AliasTemplate,
/*TemplateArgs=*/TemplateArgLists.getInnermost());

std::optional<ContextRAII> SavedContext;
if (!AliasTemplate->getDeclContext()->isFileContext())
SavedContext.emplace(*this, AliasTemplate->getDeclContext());
Expand Down
19 changes: 9 additions & 10 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,11 +737,11 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(

Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
TypeAliasTemplateDecl *Entity, ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange)
: InstantiatingTemplate(
SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
SemaRef, CodeSynthesisContext::TypeAliasTemplateInstantiation,
PointOfInstantiation, InstantiationRange, /*Entity=*/Entity,
/*Template=*/nullptr, TemplateArgs) {}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Expand Down Expand Up @@ -983,11 +983,6 @@ void Sema::PrintInstantiationStack() {
Diags.Report(Active->PointOfInstantiation,
diag::note_template_class_instantiation_here)
<< CTD << Active->InstantiationRange;
} else {
Diags.Report(Active->PointOfInstantiation,
diag::note_template_type_alias_instantiation_here)
<< cast<TypeAliasTemplateDecl>(D)
<< Active->InstantiationRange;
}
break;
}
Expand Down Expand Up @@ -1262,6 +1257,10 @@ void Sema::PrintInstantiationStack() {
diag::note_building_deduction_guide_here);
break;
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
Diags.Report(Active->PointOfInstantiation,
diag::note_template_type_alias_instantiation_here)
<< cast<TypeAliasTemplateDecl>(Active->Entity)
<< Active->InstantiationRange;
break;
}
}
Expand All @@ -1278,12 +1277,13 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
++Active)
{
switch (Active->Kind) {
case CodeSynthesisContext::TemplateInstantiation:
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
// An instantiation of an alias template may or may not be a SFINAE
// context, depending on what else is on the stack.
if (isa<TypeAliasTemplateDecl>(Active->Entity))
break;
[[fallthrough]];
case CodeSynthesisContext::TemplateInstantiation:
case CodeSynthesisContext::DefaultFunctionArgumentInstantiation:
case CodeSynthesisContext::ExceptionSpecInstantiation:
case CodeSynthesisContext::ConstraintsCheck:
Expand Down Expand Up @@ -1340,7 +1340,6 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
break;

case CodeSynthesisContext::Memoization:
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
break;
}

Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,8 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
: (TemplateArgs.begin() + TemplateArgs.getNumLevels() - 1 -
D->getTemplateDepth())
->Args);
if (InstTemplate.isInvalid())
return nullptr;

TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {
Expand Down Expand Up @@ -5091,6 +5093,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
EnterExpressionEvaluationContext EvalContext(
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);

Qualifiers ThisTypeQuals;
CXXRecordDecl *ThisContext = nullptr;
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) {
ThisContext = Method->getParent();
ThisTypeQuals = Method->getMethodQualifiers();
}
CXXThisScopeRAII ThisScope(*this, ThisContext, ThisTypeQuals);

// Introduce a new scope where local variable instantiations will be
// recorded, unless we're actually a member function within a local
// class, in which case we need to merge our results with the parent
Expand Down
25 changes: 17 additions & 8 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3307,12 +3307,13 @@ class TreeTransform {

/// Build a new C++ "this" expression.
///
/// By default, builds a new "this" expression without performing any
/// semantic analysis. Subclasses may override this routine to provide
/// different behavior.
/// By default, performs semantic analysis to build a new "this" expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildCXXThisExpr(SourceLocation ThisLoc,
QualType ThisType,
bool isImplicit) {
if (getSema().CheckCXXThisType(ThisLoc, ThisType))
return ExprError();
return getSema().BuildCXXThisExpr(ThisLoc, ThisType, isImplicit);
}

Expand Down Expand Up @@ -11177,8 +11178,8 @@ TreeTransform<Derived>::TransformDeclRefExpr(DeclRefExpr *E) {
}

if (!getDerived().AlwaysRebuild() &&
QualifierLoc == E->getQualifierLoc() &&
ND == E->getDecl() &&
!E->isCapturedByCopyInLambdaWithExplicitObjectParameter() &&
QualifierLoc == E->getQualifierLoc() && ND == E->getDecl() &&
Found == E->getFoundDecl() &&
NameInfo.getName() == E->getDecl()->getDeclName() &&
!E->hasExplicitTemplateArgs()) {
Expand Down Expand Up @@ -12641,9 +12642,17 @@ TreeTransform<Derived>::TransformCXXThisExpr(CXXThisExpr *E) {
//
// In other contexts, the type of `this` may be overrided
// for type deduction, so we need to recompute it.
QualType T = getSema().getCurLambda() ?
getDerived().TransformType(E->getType())
: getSema().getCurrentThisType();
//
// Always recompute the type if we're in the body of a lambda, and
// 'this' is dependent on a lambda's explicit object parameter.
QualType T = [&]() {
auto &S = getSema();
if (E->isCapturedByCopyInLambdaWithExplicitObjectParameter())
return S.getCurrentThisType();
if (S.getCurLambda())
return getDerived().TransformType(E->getType());
return S.getCurrentThisType();
}();

if (!getDerived().AlwaysRebuild() && T == E->getType()) {
// Mark it referenced in the new context regardless.
Expand Down
21 changes: 6 additions & 15 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ namespace clang {
GlobalDeclID NamedDeclForTagDecl = 0;
IdentifierInfo *TypedefNameForLinkage = nullptr;

bool HasPendingBody = false;

///A flag to carry the information for a decl from the entity is
/// used. We use it to delay the marking of the canonical decl as used until
/// the entire declaration is deserialized and merged.
Expand Down Expand Up @@ -314,9 +312,6 @@ namespace clang {
static void markIncompleteDeclChainImpl(Redeclarable<DeclT> *D);
static void markIncompleteDeclChainImpl(...);

/// Determine whether this declaration has a pending body.
bool hasPendingBody() const { return HasPendingBody; }

void ReadFunctionDefinition(FunctionDecl *FD);
void Visit(Decl *D);

Expand Down Expand Up @@ -541,7 +536,6 @@ void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
}
// Store the offset of the body so we can lazily load it later.
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
HasPendingBody = true;
}

void ASTDeclReader::Visit(Decl *D) {
Expand Down Expand Up @@ -1164,7 +1158,6 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
// Load the body on-demand. Most clients won't care, because method
// definitions rarely show up in headers.
Reader.PendingBodies[MD] = GetCurrentCursorOffset();
HasPendingBody = true;
}
MD->setSelfDecl(readDeclAs<ImplicitParamDecl>());
MD->setCmdDecl(readDeclAs<ImplicitParamDecl>());
Expand Down Expand Up @@ -4156,8 +4149,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
// AST consumer might need to know about, queue it.
// We don't pass it to the consumer immediately because we may be in recursive
// loading, and some declarations may still be initializing.
PotentiallyInterestingDecls.push_back(
InterestingDecl(D, Reader.hasPendingBody()));
PotentiallyInterestingDecls.push_back(D);

return D;
}
Expand All @@ -4179,10 +4171,10 @@ void ASTReader::PassInterestingDeclsToConsumer() {
EagerlyDeserializedDecls.clear();

while (!PotentiallyInterestingDecls.empty()) {
InterestingDecl D = PotentiallyInterestingDecls.front();
Decl *D = PotentiallyInterestingDecls.front();
PotentiallyInterestingDecls.pop_front();
if (isConsumerInterestedIn(getContext(), D.getDecl(), D.hasPendingBody()))
PassInterestingDeclToConsumer(D.getDecl());
if (isConsumerInterestedIn(getContext(), D, PendingBodies.count(D)))
PassInterestingDeclToConsumer(D);
}
}

Expand Down Expand Up @@ -4239,9 +4231,8 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
// We might have made this declaration interesting. If so, remember that
// we need to hand it off to the consumer.
if (!WasInteresting &&
isConsumerInterestedIn(getContext(), D, Reader.hasPendingBody())) {
PotentiallyInterestingDecls.push_back(
InterestingDecl(D, Reader.hasPendingBody()));
isConsumerInterestedIn(getContext(), D, PendingBodies.count(D))) {
PotentiallyInterestingDecls.push_back(D);
WasInteresting = true;
}
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,7 @@ void ASTStmtReader::VisitCXXThisExpr(CXXThisExpr *E) {
VisitExpr(E);
E->setLocation(readSourceLocation());
E->setImplicit(Record.readInt());
E->setCapturedByCopyInLambdaWithExplicitObjectParameter(Record.readInt());
}

void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,7 @@ void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) {
VisitExpr(E);
Record.AddSourceLocation(E->getLocation());
Record.push_back(E->isImplicit());
Record.push_back(E->isCapturedByCopyInLambdaWithExplicitObjectParameter());

Code = serialization::EXPR_CXX_THIS;
}
Expand Down
16 changes: 8 additions & 8 deletions clang/test/APINotes/retain-count-convention.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

#import <SimpleKit/SimpleKit.h>

// CHECK: void *getCFOwnedToUnowned(void) __attribute__((cf_returns_not_retained));
// CHECK: void *getCFUnownedToOwned(void) __attribute__((cf_returns_retained));
// CHECK: void *getCFOwnedToNone(void) __attribute__((cf_unknown_transfer));
// CHECK: id getObjCOwnedToUnowned(void) __attribute__((ns_returns_not_retained));
// CHECK: id getObjCUnownedToOwned(void) __attribute__((ns_returns_retained));
// CHECK: int indirectGetCFOwnedToUnowned(void * _Nullable *out __attribute__((cf_returns_not_retained)));
// CHECK: int indirectGetCFUnownedToOwned(void * _Nullable *out __attribute__((cf_returns_retained)));
// CHECK: __attribute__((cf_returns_not_retained)) void *getCFOwnedToUnowned(void);
// CHECK: __attribute__((cf_returns_retained)) void *getCFUnownedToOwned(void);
// CHECK: __attribute__((cf_unknown_transfer)) void *getCFOwnedToNone(void);
// CHECK: __attribute__((ns_returns_not_retained)) id getObjCOwnedToUnowned(void);
// CHECK: __attribute__((ns_returns_retained)) id getObjCUnownedToOwned(void);
// CHECK: int indirectGetCFOwnedToUnowned(__attribute__((cf_returns_not_retained)) void * _Nullable *out);
// CHECK: int indirectGetCFUnownedToOwned(__attribute__((cf_returns_retained)) void * _Nullable *out);
// CHECK: int indirectGetCFOwnedToNone(void * _Nullable *out);
// CHECK: int indirectGetCFNoneToOwned(void **out __attribute__((cf_returns_not_retained)));
// CHECK: int indirectGetCFNoneToOwned(__attribute__((cf_returns_not_retained)) void **out);

// CHECK-LABEL: @interface MethodTest
// CHECK: - (id)getOwnedToUnowned __attribute__((ns_returns_not_retained));
Expand Down
4 changes: 2 additions & 2 deletions clang/test/APINotes/versioned.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#import <VersionedKit/VersionedKit.h>

// CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
// CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
// CHECK-VERSIONED:__attribute__((swift_name("moveTo(a:b:)"))) void moveToPointDUMP(double x, double y);

// CHECK-DUMP-LABEL: Dumping moveToPointDUMP
// CHECK-VERSIONED-DUMP: SwiftVersionedAdditionAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
Expand Down Expand Up @@ -65,7 +65,7 @@

// CHECK-DUMP-NOT: Dumping

// CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape)));
// CHECK-UNVERSIONED: void acceptClosure(__attribute__((noescape)) void (^block)(void));
// CHECK-VERSIONED: void acceptClosure(void (^block)(void));

// CHECK-UNVERSIONED: void privateFunc(void) __attribute__((swift_private));
Expand Down
4 changes: 4 additions & 0 deletions clang/test/AST/Interp/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,3 +566,7 @@ char melchizedek[2200000000];
typedef decltype(melchizedek[1] - melchizedek[0]) ptrdiff_t;
constexpr ptrdiff_t d1 = &melchizedek[0x7fffffff] - &melchizedek[0]; // ok
constexpr ptrdiff_t d3 = &melchizedek[0] - &melchizedek[0x80000000u]; // ok

/// GH#88018
const int SZA[] = {};
void testZeroSizedArrayAccess() { unsigned c = SZA[4]; }
2 changes: 2 additions & 0 deletions clang/test/AST/Interp/ms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@

/// Used to assert because the two parameters to _rotl do not have the same type.
static_assert(_rotl(0x01, 5) == 32);

static_assert(alignof(__unaligned int) == 1, "");
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/weak.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s




/// FIXME: The new interpreter also emits the "address of weak declaration" note in the pointer-to-bool case.

[[gnu::weak]] extern int a;
int ha[(bool)&a]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
// expected-note {{comparison against address of weak declaration}} \
// both-error {{variable length array declaration not allowed at file scope}}
int ha2[&a == nullptr]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
// both-note {{comparison against address of weak declaration '&a' can only be performed at runtime}} \
// both-error {{variable length array declaration not allowed at file scope}}
2 changes: 1 addition & 1 deletion clang/test/AST/ast-print-method-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct DefMethodsWithoutBody {
// CHECK-NEXT: DefMethodsWithoutBody() = default;
~DefMethodsWithoutBody() = default;

// CHECK-NEXT: __attribute__((alias("X"))) void m1();
// CHECK-NEXT: void m1() __attribute__((alias("X")));
void m1() __attribute__((alias("X")));

// CHECK-NEXT: };
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ast-print-no-sanitize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ void should_not_crash_1() __attribute__((no_sanitize_memory));
[[clang::no_sanitize_memory]] void should_not_crash_2();

// CHECK: void should_not_crash_1() __attribute__((no_sanitize("memory")));
// CHECK: void should_not_crash_2() {{\[\[}}clang::no_sanitize("memory"){{\]\]}};
// CHECK: {{\[\[}}clang::no_sanitize("memory"){{\]\]}} void should_not_crash_2();
15 changes: 15 additions & 0 deletions clang/test/AST/attr-print-emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,18 @@ class C {
// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
};

#define ANNOTATE_ATTR __attribute__((annotate("Annotated")))
ANNOTATE_ATTR int annotated_attr ANNOTATE_ATTR = 0;
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr __attribute__((annotate("Annotated"))) = 0;

// FIXME: We do not print the attribute as written after the type specifier.
int ANNOTATE_ATTR annotated_attr_fixme = 0;
// CHECK: __attribute__((annotate("Annotated"))) int annotated_attr_fixme = 0;

#define NONNULL_ATTR __attribute__((nonnull(1)))
ANNOTATE_ATTR NONNULL_ATTR void fn_non_null_annotated_attr(int *) __attribute__((annotate("AnnotatedRHS")));
// CHECK:__attribute__((annotate("Annotated"))) __attribute__((nonnull(1))) void fn_non_null_annotated_attr(int *) __attribute__((annotate("AnnotatedRHS")));

[[gnu::nonnull(1)]] [[gnu::always_inline]] void cxx11_attr(int*) ANNOTATE_ATTR;
// CHECK: {{\[\[}}gnu::nonnull(1)]] {{\[\[}}gnu::always_inline]] void cxx11_attr(int *) __attribute__((annotate("Annotated")));
2 changes: 1 addition & 1 deletion clang/test/Analysis/scopes-cfg-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,7 @@ void test_cleanup_functions2(int m) {
// CHECK: [B1]
// CHECK-NEXT: 1: CFGScopeBegin(f)
// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], F)
// CHECK-NEXT: 3: __attribute__((cleanup(cleanup_F))) F f;
// CHECK-NEXT: 3: F f __attribute__((cleanup(cleanup_F)));
// CHECK-NEXT: 4: CleanupFunction (cleanup_F)
// CHECK-NEXT: 5: [B1.3].~F() (Implicit destructor)
// CHECK-NEXT: 6: CFGScopeEnd(f)
Expand Down
14 changes: 14 additions & 0 deletions clang/test/C/C11/n1514.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -verify -std=c11 %s
// expected-no-diagnostics

/* WG14 N1514: Yes
* Conditional normative status for Annex G
*/

// We don't support Annex G (which introduces imaginary types), but support for
// this annex is conditional in C11. So we can test for conformance to this
// paper by ensuring we don't define the macro claiming we support Annex G.

#ifdef __STDC_IEC_559_COMPLEX__
#error "when did this happen??"
#endif
6 changes: 0 additions & 6 deletions clang/test/CodeGen/PowerPC/builtins-ppc-xlcompat-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ void test_trap(void) {
__tw(ia, ib, 0); //expected-error {{argument value 0 is outside the valid range [1, 31]}}
}

#ifdef __PPC64__
void test_builtin_ppc_rldimi() {
unsigned int shift;
unsigned long long mask;
Expand All @@ -33,7 +32,6 @@ void test_builtin_ppc_rldimi() {
res = __builtin_ppc_rldimi(ull, ull, 63, 0xFFFF000000000F00); // expected-error {{argument 3 value should represent a contiguous bit field}}
res = __builtin_ppc_rldimi(ull, ull, 64, 0xFFFF000000000000); // expected-error {{argument value 64 is outside the valid range [0, 63]}}
}
#endif

void test_builtin_ppc_rlwimi() {
unsigned int shift;
Expand Down Expand Up @@ -86,10 +84,6 @@ void testalignx(const void *pointer, unsigned int alignment) {
}

#ifndef __PPC64__
unsigned long long testrldimi32() {
return __rldimi(ull, ui, 3, 0x7ffff8ULL); //expected-error {{this builtin is only available on 64-bit targets}}
}

long long testbpermd(long long bit_selector, long long source) {
return __bpermd(bit_selector, source); //expected-error {{this builtin is only available on 64-bit targets}}
}
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGen/ubsan-bitfield-conversion.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void foo1(int x) {
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -29,7 +29,7 @@ void foo2(int x) {
// CHECK-NEXT: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 6
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 6
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -42,7 +42,7 @@ void foo3() {
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -55,7 +55,7 @@ void foo4(int x) {
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
73 changes: 73 additions & 0 deletions clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,76 @@ void test_temporary() {
//CHECK: %ref.tmp = alloca %struct.MaterializedTemporary, align 1
//CHECK: call void @_ZN21MaterializedTemporaryC1Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
//CHECK invoke void @_ZNH21MaterializedTemporary3fooEOS_(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}

namespace GH86399 {
volatile int a = 0;
struct function {
function& operator=(function const&) {
a = 1;
return *this;
}
};

void f() {
function list;

//CHECK-LABEL: define internal void @"_ZZN7GH863991f{{.*}}"(ptr %{{.*}})
//CHECK: call {{.*}} @_ZN7GH863998functionaSERKS0_
//CHECK-NEXT: ret void
[&list](this auto self) {
list = function{};
}();
}
}

namespace GH84163 {
// Just check that this doesn't crash (we were previously not instantiating
// everything that needs instantiating in here).
template <typename> struct S {};

void a() {
int x;
const auto l = [&x](this auto&) { S<decltype(x)> q; };
l();
}
}

namespace GH84425 {
// As above.
void do_thing(int x) {
auto second = [&](this auto const& self, int b) -> int {
if (x) return x;
else return self(x);
};

second(1);
}

void do_thing2(int x) {
auto second = [&](this auto const& self) {
if (true) return x;
else return x;
};

second();
}
}

namespace GH79754 {
// As above.
void f() {
int x;
[&x](this auto&&) {return x;}();
}
}

namespace GH70604 {
auto dothing(int num)
{
auto fun = [&num](this auto&& self) -> void {
auto copy = num;
};

fun();
}
}
16 changes: 8 additions & 8 deletions clang/test/CodeGenCXX/ubsan-bitfield-conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ void foo1(int x) {
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
c.a = x;
// CHECK: store i8 %{{.*}}
// CHECK-BITFIELD-CONVERSION: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 5
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -42,13 +42,13 @@ void foo2(int x) {
// CHECK-BITFIELD-CONVERSION: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 6
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 6
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
c.b = x;
// CHECK: store i8 %{{.*}}
// CHECK-BITFIELD-CONVERSION: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 6
// CHECK-BITFIELD-CONVERSION-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 6
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -61,14 +61,14 @@ void foo3() {
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
c.a++;
// CHECK: store i8 %{{.*}}
// CHECK-NEXT: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 5
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
Expand All @@ -81,14 +81,14 @@ void foo4(int x) {
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
c.a += x;
// CHECK: store i8 %{{.*}}
// CHECK-NEXT: [[BFRESULTSHL:%.*]] = shl i8 {{.*}}, 5
// CHECK-NEXT: [[BFRESULTASHR:%.*]] = ashr i8 [[BFRESULTSHL]], 5
// CHECK-NEXT: [[BFRESULTCAST:%.*]] = sext i8 [[BFRESULTASHR]] to i32
// CHECK-BITFIELD-CONVERSION: call void @__ubsan_handle_implicit_conversion
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize !6
// CHECK-BITFIELD-CONVERSION-NEXT: br label %[[CONT:.*]], !nosanitize
// CHECK-BITFIELD-CONVERSION: [[CONT]]:
// CHECK-NEXT: ret void
}
2 changes: 1 addition & 1 deletion clang/test/Driver/cuda-external-tools.cu
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
// Check -Xcuda-ptxas with clang-cl
// RUN: %clang_cl -### -c -Xcuda-ptxas -foo1 \
// RUN: --offload-arch=sm_35 --cuda-path=%S/Inputs/CUDA/usr/local/cuda \
// RUN: -Xcuda-ptxas -foo2 %s 2>&1 \
// RUN: -Xcuda-ptxas -foo2 -- %s 2>&1 \
// RUN: | FileCheck -check-prefixes=CHECK,SM35,PTXAS-EXTRA %s

// MacOS spot-checks
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Lexer/cxx-features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
#error "wrong value for __cpp_char8_t"
#endif

#if check(concepts, 0, 0, 0, 0, 201907, 201907, 201907)
#if check(concepts, 0, 0, 0, 0, 202002, 202002, 202002)
#error "wrong value for __cpp_concepts"
#endif

Expand Down
3 changes: 1 addition & 2 deletions clang/test/Misc/warning-flags.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This test serves two purposes:

The list of warnings below should NEVER grow. It should gradually shrink to 0.

CHECK: Warnings without flags (67):
CHECK: Warnings without flags (66):

CHECK-NEXT: ext_expected_semi_decl_list
CHECK-NEXT: ext_explicit_specialization_storage_class
Expand Down Expand Up @@ -80,7 +80,6 @@ CHECK-NEXT: warn_register_objc_catch_parm
CHECK-NEXT: warn_related_result_type_compatibility_class
CHECK-NEXT: warn_related_result_type_compatibility_protocol
CHECK-NEXT: warn_template_export_unsupported
CHECK-NEXT: warn_tentative_incomplete_array
CHECK-NEXT: warn_typecheck_function_qualifiers
CHECK-NEXT: warn_undef_interface
CHECK-NEXT: warn_undef_interface_suggest
Expand Down
8 changes: 4 additions & 4 deletions clang/test/OpenMP/assumes_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ int lambda_outer() {
}
#pragma omp end assumes

// AST: __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void foo() {
// AST: void foo() __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) {
// AST-NEXT: }
// AST-NEXT: class BAR {
// AST-NEXT: public:
Expand All @@ -81,7 +81,7 @@ int lambda_outer() {
// AST-NEXT: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void bar() {
// AST-NEXT: BAR b;
// AST-NEXT: }
// AST-NEXT: void baz() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp")));
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void baz();
// AST-NEXT: template <typename T> class BAZ {
// AST-NEXT: public:
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) BAZ<T>() {
Expand All @@ -95,8 +95,8 @@ int lambda_outer() {
// AST-NEXT: public:
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) BAZ() {
// AST-NEXT: }
// AST-NEXT: void baz1() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp")));
// AST-NEXT: static void baz2() __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp")));
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void baz1();
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) static void baz2();
// AST-NEXT: };
// AST-NEXT: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines,ompx_another_warning,ompx_after_invalid_clauses"))) __attribute__((assume("omp_no_openmp"))) void baz() {
// AST-NEXT: BAZ<float> b;
Expand Down
2 changes: 1 addition & 1 deletion clang/test/OpenMP/assumes_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void baz() {
}
#pragma omp end assumes

// CHECK: __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void foo()
// CHECK: void foo() __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp")))
// CHECK: __attribute__((assume("ompx_range_bar_only"))) __attribute__((assume("ompx_range_bar_only_2"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void bar()
// CHECK: __attribute__((assume("ompx_1234"))) __attribute__((assume("omp_no_openmp_routines"))) __attribute__((assume("omp_no_openmp"))) void baz()

Expand Down
10 changes: 5 additions & 5 deletions clang/test/OpenMP/assumes_template_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ template <typename T>
struct S {
int a;
// CHECK: template <typename T> struct S {
// CHECK: __attribute__((assume("ompx_global_assumption"))) void foo() {
// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) {
void foo() {
#pragma omp parallel
{}
}
};

// CHECK: template<> struct S<int> {
// CHECK: __attribute__((assume("ompx_global_assumption"))) void foo() {
// CHECK: void foo() __attribute__((assume("ompx_global_assumption"))) {

#pragma omp begin assumes no_openmp
// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void S_with_assumes_no_call() {
// CHECK: __attribute__((assume("omp_no_openmp"))) void S_with_assumes_no_call() __attribute__((assume("ompx_global_assumption"))) {
void S_with_assumes_no_call() {
S<int> s;
s.a = 0;
}
// CHECK: __attribute__((assume("omp_no_openmp"))) __attribute__((assume("ompx_global_assumption"))) void S_with_assumes_call() {
// CHECK: __attribute__((assume("omp_no_openmp"))) void S_with_assumes_call() __attribute__((assume("ompx_global_assumption"))) {
void S_with_assumes_call() {
S<int> s;
s.a = 0;
Expand All @@ -42,7 +42,7 @@ void S_with_assumes_call() {
}
#pragma omp end assumes

// CHECK: __attribute__((assume("ompx_global_assumption"))) void S_without_assumes() {
// CHECK: void S_without_assumes() __attribute__((assume("ompx_global_assumption"))) {
void S_without_assumes() {
S<int> s;
s.foo();
Expand Down
4 changes: 2 additions & 2 deletions clang/test/OpenMP/declare_simd_ast_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ void h(int *hp, int *hp2, int *hq, int *lin)

class VV {
// CHECK: #pragma omp declare simd uniform(this, a) linear(val(b): a)
// CHECK-NEXT: __attribute__((cold)) int add(int a, int b) {
// CHECK-NEXT: int add(int a, int b) __attribute__((cold)) {
// CHECK-NEXT: return a + b;
// CHECK-NEXT: }
#pragma omp declare simd uniform(this, a) linear(val(b): a)
__attribute__((cold)) int add(int a, int b) { return a + b; }
int add(int a, int b) __attribute__((cold)) { return a + b; }

// CHECK: #pragma omp declare simd aligned(b: 4) aligned(a) linear(ref(b): 4) linear(val(this)) linear(val(a))
// CHECK-NEXT: float taddpf(float *a, float *&b) {
Expand Down
35 changes: 35 additions & 0 deletions clang/test/PCH/cxx23-deducing-this-lambda.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: %clang_cc1 -emit-pch -std=c++23 -o %t %s
// RUN: %clang_cc1 -include-pch %t -verify -fsyntax-only -DTEST -std=c++23 %s

// Test that dependence of 'this' and DREs due to by-value capture by a
// lambda with an explicit object parameter is serialised/deserialised
// properly.

#ifndef HEADER
#define HEADER
struct S {
int x;
auto f() {
return [*this] (this auto&&) {
int y;
x = 42;

const auto l = [y] (this auto&&) { y = 42; };
l();
};
}
};
#endif

// expected-error@* {{read-only variable is not assignable}}
// expected-error@* {{cannot assign to a variable captured by copy in a non-mutable lambda}}
// expected-note@* 2 {{in instantiation of}}

#ifdef TEST
void f() {
const auto l = S{}.f();
l(); // expected-note {{in instantiation of}}
}
#endif


5 changes: 5 additions & 0 deletions clang/test/Sema/tentative-array-decl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %clang_cc1 -verify %s
// RUN: %clang_cc1 -verify=good -Wno-tentative-definition-array %s
// good-no-diagnostics

int foo[]; // expected-warning {{tentative array definition assumed to have one element}}
6 changes: 3 additions & 3 deletions clang/test/SemaCXX/attr-no-sanitize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ int f3() __attribute__((no_sanitize("address")));

// DUMP-LABEL: FunctionDecl {{.*}} f4
// DUMP: NoSanitizeAttr {{.*}} thread
// PRINT: int f4() {{\[\[}}clang::no_sanitize("thread")]]
// PRINT: {{\[\[}}clang::no_sanitize("thread")]] int f4()
[[clang::no_sanitize("thread")]] int f4();

// DUMP-LABEL: FunctionDecl {{.*}} f4
// DUMP: NoSanitizeAttr {{.*}} hwaddress
// PRINT: int f4() {{\[\[}}clang::no_sanitize("hwaddress")]]
// PRINT: {{\[\[}}clang::no_sanitize("hwaddress")]] int f4()
[[clang::no_sanitize("hwaddress")]] int f4();

// DUMP-LABEL: FunctionDecl {{.*}} f5
Expand All @@ -36,5 +36,5 @@ int f6() __attribute__((no_sanitize("unknown"))); // expected-warning{{unknown s

// DUMP-LABEL: FunctionDecl {{.*}} f7
// DUMP: NoSanitizeAttr {{.*}} memtag
// PRINT: int f7() {{\[\[}}clang::no_sanitize("memtag")]]
// PRINT: {{\[\[}}clang::no_sanitize("memtag")]] int f7()
[[clang::no_sanitize("memtag")]] int f7();
8 changes: 4 additions & 4 deletions clang/test/SemaCXX/cxx11-attr-print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ int d [[deprecated("warning")]];
// CHECK: __attribute__((deprecated("warning", "fixit")));
int e __attribute__((deprecated("warning", "fixit")));

// CHECK: int cxx11_alignas alignas(4);
// CHECK: alignas(4) int cxx11_alignas;
alignas(4) int cxx11_alignas;

// CHECK: int c11_alignas _Alignas(int);
// CHECK: _Alignas(int) int c11_alignas;
_Alignas(int) int c11_alignas;

// CHECK: int foo() __attribute__((const));
Expand Down Expand Up @@ -66,7 +66,7 @@ void f8 (void *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
// CHECK: int n alignas(4
// CHECK: int p alignas(int
// CHECK: __attribute__((pure)) static int f()
// CHECK: static int g() {{\[}}[gnu::pure]]
// CHECK: {{\[}}[gnu::pure]] static int g()
template <typename T> struct S {
__attribute__((aligned(4))) int m;
alignas(4) int n;
Expand All @@ -82,7 +82,7 @@ template <typename T> struct S {
// CHECK: int m __attribute__((aligned(4
// CHECK: int n alignas(4
// CHECK: __attribute__((pure)) static int f()
// CHECK: static int g() {{\[}}[gnu::pure]]
// CHECK: {{\[}}[gnu::pure]] static int g()
template struct S<int>;

// CHECK: using Small2 {{\[}}[gnu::mode(byte)]] = int;
Expand Down
176 changes: 176 additions & 0 deletions clang/test/SemaCXX/cxx2b-deducing-this.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,118 @@ void TestMutationInLambda() {
[i = 0](this auto){ i++; }();
[i = 0](this const auto&){ i++; }();
// expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
// expected-note@-2 {{in instantiation of}}

int x;
const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
const auto l2 = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}

const auto l3 = [&x](this auto&) {
const auto l3a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l3a(); // expected-note {{in instantiation of}}
};

const auto l4 = [&x](this auto&) {
const auto l4a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l4a(); // expected-note {{in instantiation of}}
};

const auto l5 = [x](this auto&) {
const auto l5a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l5a(); // expected-note {{in instantiation of}}
};

const auto l6 = [=](this auto&) {
const auto l6a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l6a(); // expected-note {{in instantiation of}}
};

const auto l7 = [x](this auto&) {
const auto l7a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l7a(); // expected-note {{in instantiation of}}
};

const auto l8 = [=](this auto&) {
const auto l8a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l8a(); // expected-note {{in instantiation of}}
};

const auto l9 = [&](this auto&) {
const auto l9a = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l9a(); // expected-note {{in instantiation of}}
};

const auto l10 = [&](this auto&) {
const auto l10a = [=](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
l10a(); // expected-note {{in instantiation of}}
};

const auto l11 = [x](this auto&) {
const auto l11a = [&x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
l11a();
};

const auto l12 = [x](this auto&) {
const auto l12a = [&](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
l12a();
};

const auto l13 = [=](this auto&) {
const auto l13a = [&x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} expected-note {{while substituting}}
l13a();
};

struct S {
int x;
auto f() {
return [*this] (this auto&&) {
x = 42; // expected-error {{read-only variable is not assignable}}
[*this] () mutable { x = 42; } ();
[*this] (this auto&&) { x = 42; } ();
[*this] () { x = 42; } (); // expected-error {{read-only variable is not assignable}}
const auto l = [*this] (this auto&&) { x = 42; }; // expected-error {{read-only variable is not assignable}}
l(); // expected-note {{in instantiation of}}

struct T {
int x;
auto g() {
return [&] (this auto&&) {
x = 42;
const auto l = [*this] (this auto&&) { x = 42; }; // expected-error {{read-only variable is not assignable}}
l(); // expected-note {{in instantiation of}}
};
}
};

const auto l2 = T{}.g();
l2(); // expected-note {{in instantiation of}}
};
}
};

const auto l14 = S{}.f();

l1(); // expected-note {{in instantiation of}}
l2(); // expected-note {{in instantiation of}}
l3(); // expected-note {{in instantiation of}}
l4(); // expected-note {{in instantiation of}}
l5(); // expected-note {{in instantiation of}}
l6(); // expected-note {{in instantiation of}}
l7(); // expected-note {{in instantiation of}}
l8(); // expected-note {{in instantiation of}}
l9(); // expected-note {{in instantiation of}}
l10(); // expected-note {{in instantiation of}}
l11(); // expected-note {{in instantiation of}}
l12(); // expected-note {{in instantiation of}}
l13(); // expected-note {{in instantiation of}}
l14(); // expected-note 3 {{in instantiation of}}

{
const auto l1 = [&x](this auto&) { x = 42; };
const auto l2 = [&](this auto&) { x = 42; };
l1();
l2();
}
}

struct Over_Call_Func_Example {
Expand Down Expand Up @@ -650,3 +762,67 @@ int bug() {
S{}.f(0);
}
}

namespace GH84163 {
struct S {
int x;

auto foo() {
return [*this](this auto&&) {
x = 10; // expected-error {{read-only variable is not assignable}}
};
}
};

int f() {
S s{ 5 };
const auto l = s.foo();
l(); // expected-note {{in instantiation of}}

const auto g = [x = 10](this auto&& self) { x = 20; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
g(); // expected-note {{in instantiation of}}
}
}

namespace GH86054 {
template<typename M>
struct unique_lock {
unique_lock(M&) {}
};
int f() {
struct mutex {} cursor_guard;
[&cursor_guard](this auto self) {
unique_lock a(cursor_guard);
}();
}
}

namespace GH86398 {
struct function {}; // expected-note 2 {{not viable}}
int f() {
function list;
[&list](this auto self) {
list = self; // expected-error {{no viable overloaded '='}}
}(); // expected-note {{in instantiation of}}
}

struct function2 {
function2& operator=(function2 const&) = delete; // expected-note {{candidate function not viable}}
};
int g() {
function2 list;
[&list](this auto self) {
list = self; // expected-error {{no viable overloaded '='}}
}(); // expected-note {{in instantiation of}}
}

struct function3 {
function3& operator=(function3 const&) = delete; // expected-note {{has been explicitly deleted}}
};
int h() {
function3 list;
[&list](this auto self) {
list = function3{}; // expected-error {{selected deleted operator '='}}
}();
}
}
67 changes: 67 additions & 0 deletions clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wundefined-func-template %s

namespace GH74016 {
template <typename T> class B {
public:
constexpr void foo(const T &) { bar(1); }
virtual constexpr void bar(unsigned int) = 0;
};

template <typename T> class D : public B<T> {
public:
constexpr void bar(unsigned int) override {}
};

void test() {
auto t = D<int>();
t.foo(0);
}
};

namespace call_pure_virtual_function_from_virtual {
template <typename T> class B {
public:
const void foo(const T &) { B::bar(1); } // expected-warning {{instantiation of function 'call_pure_virtual_function_from_virtual::B<int>::bar' required here, but no definition is available}}
// expected-note@-1 {{add an explicit instantiation declaration to suppress this warning if 'call_pure_virtual_function_from_virtual::B<int>::bar' is explicitly instantiated in another translation unit}}
virtual const void bar(unsigned int) = 0; // expected-note {{forward declaration of template entity is here}}
};

template <typename T> class D : public B<T> {
public:
const void bar(unsigned int) override {}
};

void test() {
auto t = D<int>();
t.foo(0); // expected-note {{in instantiation of member function 'call_pure_virtual_function_from_virtual::B<int>::foo' requested here}}
}
};

namespace non_pure_virtual_function {
template <typename T> class B {
public:
constexpr void foo(const T &) { bar(1); }

virtual constexpr void bar(unsigned int); // expected-warning {{inline function 'non_pure_virtual_function::B<int>::bar' is not defined}}
// expected-note@-1 {{forward declaration of template entity is here}}
// expected-note@-2 {{forward declaration of template entity is here}}
// expected-note@-3 {{forward declaration of template entity is here}}
};

template <typename T> class D : public B<T> { // expected-warning {{instantiation of function 'non_pure_virtual_function::B<int>::bar' required here, but no definition is available}}
// expected-warning@-1 {{instantiation of function 'non_pure_virtual_function::B<int>::bar' required here, but no definition is available}}
// expected-warning@-2 {{instantiation of function 'non_pure_virtual_function::B<int>::bar' required here, but no definition is available}}
// expected-note@-3 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note@-4 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note@-5 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note@-6 {{used here}}

public:
constexpr void bar(unsigned int) override { }
};

void test() {
auto t = D<int>();
t.foo(0);
}
};
44 changes: 41 additions & 3 deletions clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify %s
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -Wno-unused-value -verify %s
// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -Wno-unused-value -verify %s

// expected-no-diagnostics
class A {
public:
template<class U> A(U p) {}
Expand Down Expand Up @@ -76,3 +75,42 @@ struct S {
int f<0>(int);
};
}

namespace UsesThis {
template<typename T>
struct A {
int x;

template<typename U>
static void f();

template<>
void f<int>() {
this->x; // expected-error {{invalid use of 'this' outside of a non-static member function}}
x; // expected-error {{invalid use of member 'x' in static member function}}
A::x; // expected-error {{invalid use of member 'x' in static member function}}
+x; // expected-error {{invalid use of member 'x' in static member function}}
+A::x; // expected-error {{invalid use of member 'x' in static member function}}
}

template<typename U>
void g();

template<>
void g<int>() {
this->x;
x;
A::x;
+x;
+A::x;
}

template<typename U>
static auto h() -> A*;

template<>
auto h<int>() -> decltype(this); // expected-error {{'this' cannot be used in a static member function declaration}}
};

template struct A<int>; // expected-note 2{{in instantiation of}}
}
1 change: 1 addition & 0 deletions clang/unittests/AST/Interp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_clang_unittest(InterpTests
Descriptor.cpp
toAPValue.cpp
)

clang_target_link_libraries(InterpTests
Expand Down
90 changes: 90 additions & 0 deletions clang/unittests/AST/Interp/toAPValue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "../../../lib/AST/Interp/Context.h"
#include "../../../lib/AST/Interp/Descriptor.h"
#include "../../../lib/AST/Interp/Program.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"

using namespace clang;
using namespace clang::interp;
using namespace clang::ast_matchers;

/// Test the various toAPValue implementations.
TEST(ToAPValue, Pointers) {
constexpr char Code[] =
"struct A { bool a; bool z; };\n"
"struct S {\n"
" A a[3];\n"
"};\n"
"constexpr S d = {{{true, false}, {false, true}, {false, false}}};\n"
"constexpr const bool *b = &d.a[1].z;\n";

auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-fexperimental-new-constant-interpreter"});

auto &Ctx = AST->getASTContext().getInterpContext();
Program &Prog = Ctx.getProgram();

auto getDecl = [&](const char *Name) -> const ValueDecl * {
auto Nodes =
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
assert(Nodes.size() == 1);
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
assert(D);
return D;
};
auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};

const Pointer &GP = getGlobalPtr("b");
const Pointer &P = GP.deref<Pointer>();
ASSERT_TRUE(P.isLive());
APValue A = P.toAPValue();
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.hasLValuePath());
const auto &Path = A.getLValuePath();
ASSERT_EQ(Path.size(), 3u);
ASSERT_EQ(A.getLValueBase(), getDecl("d"));
}

TEST(ToAPValue, FunctionPointers) {
constexpr char Code[] = " constexpr bool foo() { return true; }\n"
" constexpr bool (*func)() = foo;\n";

auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-fexperimental-new-constant-interpreter"});

auto &Ctx = AST->getASTContext().getInterpContext();
Program &Prog = Ctx.getProgram();

auto getDecl = [&](const char *Name) -> const ValueDecl * {
auto Nodes =
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
assert(Nodes.size() == 1);
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
assert(D);
return D;
};

auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};

const Pointer &GP = getGlobalPtr("func");
const FunctionPointer &FP = GP.deref<FunctionPointer>();
ASSERT_FALSE(FP.isZero());
APValue A = FP.toAPValue();
ASSERT_TRUE(A.hasValue());
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.hasLValuePath());
const auto &Path = A.getLValuePath();
ASSERT_EQ(Path.size(), 0u);
ASSERT_FALSE(A.getLValueBase().isNull());
ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo"));
}
2 changes: 2 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2321,6 +2321,8 @@ TEST_P(ASTMatchersTest, LambdaCaptureTest_BindsToCaptureOfVarDecl) {
matches("int main() { int cc; auto f = [=](){ return cc; }; }", matcher));
EXPECT_TRUE(
matches("int main() { int cc; auto f = [&](){ return cc; }; }", matcher));
EXPECT_TRUE(matches(
"void f(int a) { int cc[a]; auto f = [&](){ return cc;}; }", matcher));
}

TEST_P(ASTMatchersTest, LambdaCaptureTest_BindsToCaptureWithInitializer) {
Expand Down
50 changes: 24 additions & 26 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7977,39 +7977,37 @@ TEST_F(FormatTest, AllowAllArgumentsOnNextLineDontAlign) {
}

TEST_F(FormatTest, BreakFunctionDefinitionParameters) {
FormatStyle Style = getLLVMStyle();
EXPECT_FALSE(Style.BreakFunctionDefinitionParameters);
StringRef Input = "void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(int A, int B, int C) {}\n"
"Class::Class(int A, int B) : m_A(A), m_B(B) {}\n";
verifyFormat(StringRef("void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(int A, int B, int C) {}\n"
"Class::Class(int A, int B) : m_A(A), m_B(B) {}\n"),
Input, Style);
"Class::Class(int A, int B) : m_A(A), m_B(B) {}";
verifyFormat(Input);

FormatStyle Style = getLLVMStyle();
EXPECT_FALSE(Style.BreakFunctionDefinitionParameters);
Style.BreakFunctionDefinitionParameters = true;
verifyFormat(StringRef("void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(\n"
" int A, int B, int C) {}\n"
"Class::Class(\n"
" int A, int B)\n"
" : m_A(A), m_B(B) {}\n"),
verifyFormat("void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(\n"
" int A, int B, int C) {}\n"
"Class::Class(\n"
" int A, int B)\n"
" : m_A(A), m_B(B) {}",
Input, Style);
// Test the style where all parameters are on their own lines

// Test the style where all parameters are on their own lines.
Style.AllowAllParametersOfDeclarationOnNextLine = false;
Style.BinPackParameters = false;
verifyFormat(StringRef("void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(\n"
" int A,\n"
" int B,\n"
" int C) {}\n"
"Class::Class(\n"
" int A,\n"
" int B)\n"
" : m_A(A), m_B(B) {}\n"),
verifyFormat("void functionDecl(paramA, paramB, paramC);\n"
"void emptyFunctionDefinition() {}\n"
"void functionDefinition(\n"
" int A,\n"
" int B,\n"
" int C) {}\n"
"Class::Class(\n"
" int A,\n"
" int B)\n"
" : m_A(A), m_B(B) {}",
Input, Style);
}

Expand Down
103 changes: 51 additions & 52 deletions clang/unittests/Format/FormatTestTableGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ TEST_F(FormatTestTableGen, LiteralsAndIdentifiers) {
" let 0startID = $TokVarName;\n"
" let 0xstartInteger = 0x42;\n"
" let someIdentifier = $TokVarName;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, BangOperators) {
Expand Down Expand Up @@ -101,22 +101,22 @@ TEST_F(FormatTestTableGen, BangOperators) {
" \"zerozero\",\n"
" true: // default\n"
" \"positivepositive\");\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, Include) {
verifyFormat("include \"test/IncludeFile.h\"\n");
verifyFormat("include \"test/IncludeFile.h\"");
}

TEST_F(FormatTestTableGen, Types) {
verifyFormat("def Types : list<int>, bits<3>, list<list<string>> {}\n");
verifyFormat("def Types : list<int>, bits<3>, list<list<string>> {}");
}

TEST_F(FormatTestTableGen, SimpleValue1_SingleLiterals) {
verifyFormat("def SimpleValue {\n"
" let Integer = 42;\n"
" let String = \"some string\";\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, SimpleValue1_MultilineString) {
Expand All @@ -129,7 +129,7 @@ TEST_F(FormatTestTableGen, SimpleValue1_MultilineString) {
"delimited by \\[{ and }\\]. It can break across lines and the line "
"breaks are retained in the string. \n"
"(https://llvm.org/docs/TableGen/ProgRef.html#grammar-token-TokCode)}];\n"
"}\n";
"}";
StringRef DefWithCodeMessedUp =
"def SimpleValueCode { let \n"
"Code= \n"
Expand All @@ -139,23 +139,23 @@ TEST_F(FormatTestTableGen, SimpleValue1_MultilineString) {
"breaks are retained in the string. \n"
"(https://llvm.org/docs/TableGen/ProgRef.html#grammar-token-TokCode)}] \n"
" ; \n"
" } \n";
" } ";
verifyFormat(DefWithCode, DefWithCodeMessedUp);
}

TEST_F(FormatTestTableGen, SimpleValue2) {
verifyFormat("def SimpleValue2 {\n"
" let True = true;\n"
" let False = false;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, SimpleValue3) {
verifyFormat("class SimpleValue3<int x> { int Question = ?; }\n");
verifyFormat("class SimpleValue3<int x> { int Question = ?; }");
}

TEST_F(FormatTestTableGen, SimpleValue4) {
verifyFormat("def SimpleValue4 { let ValueList = {1, 2, 3}; }\n");
verifyFormat("def SimpleValue4 { let ValueList = {1, 2, 3}; }");
}

TEST_F(FormatTestTableGen, SimpleValue5) {
Expand All @@ -166,7 +166,7 @@ TEST_F(FormatTestTableGen, SimpleValue5) {
" list<int>>;\n"
" let SquareBitsListWithType = [ {1, 2},\n"
" {3, 4} ]<list<bits<8>>>;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, SimpleValue6) {
Expand All @@ -184,15 +184,15 @@ TEST_F(FormatTestTableGen, SimpleValue6) {
" );\n"
" let DAGArgBang = (!cast<SomeType>(\"Some\") i32:$src1,\n"
" i32:$src2);\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, SimpleValue7) {
verifyFormat("def SimpleValue7 { let Identifier = SimpleValue; }\n");
verifyFormat("def SimpleValue7 { let Identifier = SimpleValue; }");
}

TEST_F(FormatTestTableGen, SimpleValue8) {
verifyFormat("def SimpleValue8 { let Class = SimpleValue3<3>; }\n");
verifyFormat("def SimpleValue8 { let Class = SimpleValue3<3>; }");
}

TEST_F(FormatTestTableGen, ValueSuffix) {
Expand All @@ -203,19 +203,18 @@ TEST_F(FormatTestTableGen, ValueSuffix) {
" let Slice1 = value[1, ];\n"
" let Slice2 = value[4...7, 17, 2...3, 4];\n"
" let Field = value.field;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, PasteOperator) {
verifyFormat(
"def Paste#\"Operator\" { string Paste = \"Paste\"#operator; }\n");
verifyFormat("def Paste#\"Operator\" { string Paste = \"Paste\"#operator; }");

verifyFormat("def [\"Traring\", \"Paste\"]# {\n"
" string X = Traring#;\n"
" string Y = List<\"Operator\">#;\n"
" string Z = [\"Traring\", \"Paste\", \"Traring\", \"Paste\",\n"
" \"Traring\", \"Paste\"]#;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, ClassDefinition) {
Expand All @@ -229,9 +228,9 @@ TEST_F(FormatTestTableGen, ClassDefinition) {
" defvar Item6 = 6;\n"
" let Item7 = ?;\n"
" assert !ge(x, 0), \"Assert7\";\n"
"}\n");
"}");

verifyFormat("class FPFormat<bits<3> val> { bits<3> Value = val; }\n");
verifyFormat("class FPFormat<bits<3> val> { bits<3> Value = val; }");
}

TEST_F(FormatTestTableGen, Def) {
Expand All @@ -240,18 +239,18 @@ TEST_F(FormatTestTableGen, Def) {
" let Item2{1, 3...4} = {1, 2};\n"
" defvar Item3 = (ops nodty:$node1, nodty:$node2);\n"
" assert !le(Item2, 0), \"Assert4\";\n"
"}\n");
"}");

verifyFormat("class FPFormat<bits<3> val> { bits<3> Value = val; }\n");
verifyFormat("class FPFormat<bits<3> val> { bits<3> Value = val; }");

verifyFormat("def NotFP : FPFormat<0>;\n");
verifyFormat("def NotFP : FPFormat<0>;");
}

TEST_F(FormatTestTableGen, Let) {
verifyFormat("let x = 1, y = value<type>,\n"
" z = !and(!gt(!add(1, 2), !sub(3, 4)), !isa<Ty>($x)) in {\n"
" class Class1 : Parent<x, y> { let Item1 = z; }\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, MultiClass) {
Expand Down Expand Up @@ -287,7 +286,7 @@ TEST_F(FormatTestTableGen, MultiClass) {
" }\n"
" }\n"
" }\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, MultiClassesWithPasteOperator) {
Expand All @@ -297,25 +296,25 @@ TEST_F(FormatTestTableGen, MultiClassesWithPasteOperator) {
" def : Def#x<i>;\n"
" def : Def#y<i>;\n"
"}\n"
"multiclass MultiClass2<int i> { def : Def#x<i>; }\n");
"multiclass MultiClass2<int i> { def : Def#x<i>; }");
}

TEST_F(FormatTestTableGen, Defm) {
verifyFormat("defm : Multiclass<0>;\n");
verifyFormat("defm : Multiclass<0>;");

verifyFormat("defm Defm1 : Multiclass<1>;\n");
verifyFormat("defm Defm1 : Multiclass<1>;");
}

TEST_F(FormatTestTableGen, Defset) {
verifyFormat("defset list<Class> DefSet1 = {\n"
" def Def1 : Class<1>;\n"
" def Def2 : Class<2>;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, Defvar) {
verifyFormat("defvar DefVar1 = !cond(!ge(!size(PaseOperator.Paste), 1): 1,\n"
" true: 0);\n");
" true: 0);");
}

TEST_F(FormatTestTableGen, ForEach) {
Expand All @@ -325,21 +324,21 @@ TEST_F(FormatTestTableGen, ForEach) {
" (!if(!lt(x, i),\n"
" !shl(!mul(x, i), !size(\"string\")),\n"
" !size(!strconcat(\"a\", \"b\", \"c\"))))>;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, Dump) { verifyFormat("dump \"Dump\";\n"); }
TEST_F(FormatTestTableGen, Dump) { verifyFormat("dump \"Dump\";"); }

TEST_F(FormatTestTableGen, If) {
verifyFormat("if !gt(x, 0) then {\n"
" def : IfThen<x>;\n"
"} else {\n"
" def : IfElse<x>;\n"
"}\n");
"}");
}

TEST_F(FormatTestTableGen, Assert) {
verifyFormat("assert !le(DefVar1, 0), \"Assert1\";\n");
verifyFormat("assert !le(DefVar1, 0), \"Assert1\";");
}

TEST_F(FormatTestTableGen, DAGArgBreakElements) {
Expand All @@ -349,33 +348,33 @@ TEST_F(FormatTestTableGen, DAGArgBreakElements) {
ASSERT_EQ(Style.TableGenBreakInsideDAGArg, FormatStyle::DAS_DontBreak);
verifyFormat("def Def : Parent {\n"
" let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n"
"}\n",
"}",
Style);
// This option forces to break inside the DAGArg.
Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakElements;
verifyFormat("def Def : Parent {\n"
" let dagarg = (ins a:$src1,\n"
" aa:$src2,\n"
" aaa:$src3);\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (other a:$src1,\n"
" aa:$src2,\n"
" aaa:$src3);\n"
"}\n",
"}",
Style);
// Then, limit the DAGArg operator only to "ins".
Style.TableGenBreakingDAGArgOperators = {"ins"};
verifyFormat("def Def : Parent {\n"
" let dagarg = (ins a:$src1,\n"
" aa:$src2,\n"
" aaa:$src3);\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (other a:$src1, aa:$src2, aaa:$src3)\n"
"}\n",
"}",
Style);
}

Expand All @@ -385,7 +384,7 @@ TEST_F(FormatTestTableGen, DAGArgBreakAll) {
// By default, the DAGArg does not have a break inside.
verifyFormat("def Def : Parent {\n"
" let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n"
"}\n",
"}",
Style);
// This option forces to break inside the DAGArg.
Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakAll;
Expand All @@ -395,15 +394,15 @@ TEST_F(FormatTestTableGen, DAGArgBreakAll) {
" aa:$src2,\n"
" aaa:$src3\n"
" );\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (other\n"
" a:$src1,\n"
" aa:$src2,\n"
" aaa:$src3\n"
" );\n"
"}\n",
"}",
Style);
// Then, limit the DAGArg operator only to "ins".
Style.TableGenBreakingDAGArgOperators = {"ins"};
Expand All @@ -413,11 +412,11 @@ TEST_F(FormatTestTableGen, DAGArgBreakAll) {
" aa:$src2,\n"
" aaa:$src3\n"
" );\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (other a:$src1, aa:$src2, aaa:$src3);\n"
"}\n",
"}",
Style);
}

Expand All @@ -432,11 +431,11 @@ TEST_F(FormatTestTableGen, DAGArgAlignment) {
" aa:$src2,\n"
" aaa:$src3\n"
" )\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (not a:$src1, aa:$src2, aaa:$src2)\n"
"}\n",
"}",
Style);
Style.AlignConsecutiveTableGenBreakingDAGArgColons.Enabled = true;
verifyFormat("def Def : Parent {\n"
Expand All @@ -445,11 +444,11 @@ TEST_F(FormatTestTableGen, DAGArgAlignment) {
" aa :$src2,\n"
" aaa:$src3\n"
" )\n"
"}\n",
"}",
Style);
verifyFormat("def Def : Parent {\n"
" let dagarg = (not a:$src1, aa:$src2, aaa:$src2)\n"
"}\n",
"}",
Style);
}

Expand All @@ -458,12 +457,12 @@ TEST_F(FormatTestTableGen, CondOperatorAlignment) {
Style.ColumnLimit = 60;
verifyFormat("let CondOpe1 = !cond(!eq(size, 1): 1,\n"
" !eq(size, 16): 1,\n"
" true: 0);\n",
" true: 0);",
Style);
Style.AlignConsecutiveTableGenCondOperatorColons.Enabled = true;
verifyFormat("let CondOpe1 = !cond(!eq(size, 1) : 1,\n"
" !eq(size, 16): 1,\n"
" true : 0);\n",
" true : 0);",
Style);
}

Expand All @@ -472,12 +471,12 @@ TEST_F(FormatTestTableGen, DefAlignment) {
Style.ColumnLimit = 60;
verifyFormat("def Def : Parent {}\n"
"def DefDef : Parent {}\n"
"def DefDefDef : Parent {}\n",
"def DefDefDef : Parent {}",
Style);
Style.AlignConsecutiveTableGenDefinitionColons.Enabled = true;
verifyFormat("def Def : Parent {}\n"
"def DefDef : Parent {}\n"
"def DefDefDef : Parent {}\n",
"def DefDefDef : Parent {}",
Style);
}

Expand Down
Loading