diff --git a/clang/include/clang/AST/CurrentSourceLocExprScope.h b/clang/include/clang/AST/CurrentSourceLocExprScope.h --- a/clang/include/clang/AST/CurrentSourceLocExprScope.h +++ b/clang/include/clang/AST/CurrentSourceLocExprScope.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_AST_CURRENTSOURCELOCEXPRSCOPE_H #define LLVM_CLANG_AST_CURRENTSOURCELOCEXPRSCOPE_H +#include "clang/AST/Expr.h" +#include "llvm/ADT/Optional.h" #include namespace clang { @@ -23,8 +25,9 @@ /// value of the source location builtins (ex. __builtin_LINE), including the /// context of default argument and default initializer expressions. class CurrentSourceLocExprScope { - /// The CXXDefaultArgExpr or CXXDefaultInitExpr we're currently evaluating. - const Expr *DefaultExpr = nullptr; + /// The source locations coming from CXXDefaultArgExpr or CXXDefaultInitExpr + /// we're currently evaluating. + llvm::Optional Context; public: /// A RAII style scope guard used for tracking the current source @@ -32,27 +35,23 @@ /// (ex. __builtin_LINE). class SourceLocExprScopeGuard; - const Expr *getDefaultExpr() const { return DefaultExpr; } + llvm::Optional getSLocs() const { return Context; } explicit CurrentSourceLocExprScope() = default; private: - explicit CurrentSourceLocExprScope(const Expr *DefaultExpr) - : DefaultExpr(DefaultExpr) {} - - CurrentSourceLocExprScope(CurrentSourceLocExprScope const &) = default; - CurrentSourceLocExprScope & - operator=(CurrentSourceLocExprScope const &) = default; + explicit CurrentSourceLocExprScope( + llvm::Optional Context) + : Context(std::move(Context)) {} }; class CurrentSourceLocExprScope::SourceLocExprScopeGuard { public: - SourceLocExprScopeGuard(const Expr *DefaultExpr, + SourceLocExprScopeGuard(SourceLocExpr::Context Context, CurrentSourceLocExprScope &Current) : Current(Current), OldVal(Current), Enable(false) { - assert(DefaultExpr && "the new scope should not be empty"); - if ((Enable = (Current.getDefaultExpr() == nullptr))) - Current = CurrentSourceLocExprScope(DefaultExpr); + if ((Enable = (!Current.getSLocs().has_value()))) + Current = CurrentSourceLocExprScope(Context); } ~SourceLocExprScopeGuard() { diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -18,6 +18,7 @@ #include "clang/AST/ComputeDependence.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclAccessPair.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DependenceFlags.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/Stmt.h" @@ -55,6 +56,7 @@ class StringLiteral; class TargetInfo; class ValueDecl; + class CurrentSourceLocExprScope; /// A simple array of base specifiers. typedef SmallVector CXXCastPath; @@ -732,9 +734,9 @@ /// Evaluate an expression that is required to be a constant expression. Does /// not check the syntactic constraints for C and C++98 constant expressions. - bool EvaluateAsConstantExpr( - EvalResult &Result, const ASTContext &Ctx, - ConstantExprKind Kind = ConstantExprKind::Normal) const; + bool EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, + ConstantExprKind Kind = ConstantExprKind::Normal, + CurrentSourceLocExprScope *SLocs = nullptr) const; /// If the current Expr is a pointer, this will try to statically /// determine the number of bytes available where the pointer is pointing. @@ -4689,10 +4691,18 @@ /// Build an empty call expression. explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + struct Context { + SourceLocation UsedLocation; + const DeclContext *UsedContext = nullptr; + + static Context FromDefaultArg(const CXXDefaultArgExpr* E); + static Context FromDefaultInit(const CXXDefaultInitExpr* E); + }; + /// Return the result of evaluating this SourceLocExpr in the specified /// (and possibly null) default argument or initialization context. APValue EvaluateInContext(const ASTContext &Ctx, - const Expr *DefaultExpr) const; + llvm::Optional LocCtx) const; /// Return a string representing the name of the specific builtin function. StringRef getBuiltinStr() const; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -1238,8 +1238,11 @@ /// This wraps up a function call argument that was created from the /// corresponding parameter's default argument, when the call did not /// explicitly supply arguments for all of the parameters. -class CXXDefaultArgExpr final : public Expr { +class CXXDefaultArgExpr final + : public Expr, + private llvm::TrailingObjects { friend class ASTStmtReader; + friend TrailingObjects; /// The parameter whose default is being used. ParmVarDecl *Param; @@ -1247,8 +1250,13 @@ /// The context where the default argument expression was used. DeclContext *UsedContext; + CXXDefaultArgExpr(EmptyShell Empty, bool HasRewrittenArg) + : Expr(CXXDefaultArgExprClass, Empty) { + CXXDefaultArgExprBits.HasRewrittenArg = HasRewrittenArg; + } + CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param, - DeclContext *UsedContext) + DeclContext *UsedContext, Expr* RewrittenArg) : Expr(SC, Param->hasUnparsedDefaultArg() ? Param->getType().getNonReferenceType() @@ -1256,29 +1264,54 @@ Param->getDefaultArg()->getValueKind(), Param->getDefaultArg()->getObjectKind()), Param(Param), UsedContext(UsedContext) { + CXXDefaultArgExprBits.HasRewrittenArg = !!RewrittenArg; CXXDefaultArgExprBits.Loc = Loc; + if (RewrittenArg) + *getTrailingObjects() = RewrittenArg; setDependence(computeDependence(this)); } + size_t numTrailingObjects() const { + return CXXDefaultArgExprBits.HasRewrittenArg; + } + public: - CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {} + + static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C, bool HasRewrittenArg) { + size_t Size = totalSizeToAlloc(HasRewrittenArg); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(EmptyShell(), HasRewrittenArg); + } // \p Param is the parameter whose default argument is used by this // expression. static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc, ParmVarDecl *Param, - DeclContext *UsedContext) { - return new (C) - CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext); + DeclContext *UsedContext, + Expr* RewrittenArg = nullptr) { + size_t Size = totalSizeToAlloc(RewrittenArg != nullptr); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, + UsedContext, RewrittenArg); } + bool hasRewrittenArg() const { return CXXDefaultArgExprBits.HasRewrittenArg; } + // Retrieve the parameter that the argument was created from. const ParmVarDecl *getParam() const { return Param; } ParmVarDecl *getParam() { return Param; } // Retrieve the actual argument to the function call. - const Expr *getExpr() const { return getParam()->getDefaultArg(); } - Expr *getExpr() { return getParam()->getDefaultArg(); } + const Expr *getExpr() const { + if (CXXDefaultArgExprBits.HasRewrittenArg) + return *getTrailingObjects(); + return getParam()->getDefaultArg(); + } + Expr *getExpr() { + if (CXXDefaultArgExprBits.HasRewrittenArg) + return *getTrailingObjects(); + return getParam()->getDefaultArg(); + } const DeclContext *getUsedContext() const { return UsedContext; } DeclContext *getUsedContext() { return UsedContext; } diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -686,6 +686,9 @@ unsigned : NumExprBits; + /// Whether this CXXDefaultArgExpr rewrote its argument and stores a copy. + unsigned HasRewrittenArg : 1; + /// The location where the default argument expression was used. SourceLocation Loc; }; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -248,6 +248,7 @@ void VisitCaseStmt(const CaseStmt *Node); void VisitCompoundStmt(const CompoundStmt *Node); void VisitConstantExpr(const ConstantExpr *Node); + void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node); void VisitCallExpr(const CallExpr *Node); void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node); void VisitCastExpr(const CastExpr *Node); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -19,6 +19,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Availability.h" #include "clang/AST/ComparisonCategories.h" +#include "clang/AST/CurrentSourceLocExprScope.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" @@ -1360,10 +1361,16 @@ ExpressionEvaluationContext::ImmediateFunctionContext && InDiscardedStatement); } + + bool isPotentiallyEvaluatedIfUsed() const { + return Context == ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; + } }; /// A stack of expression evaluation contexts. SmallVector ExprEvalContexts; + /// Used to evaluate source locations. + CurrentSourceLocExprScope* OverridenSourceLocs = nullptr; /// Emit a warning for all pending noderef expressions that we recorded. void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); @@ -6126,7 +6133,7 @@ /// Instantiate or parse a C++ default argument expression as necessary. /// Return true on error. bool CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, - ParmVarDecl *Param); + ParmVarDecl *Param, Expr *&RewrittenArg); /// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating /// the default expr if needed. @@ -9436,6 +9443,12 @@ return ExprEvalContexts.back().isImmediateFunctionContext(); } + bool isPotentiallyEvaluatedIfUsedContext() const { + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return ExprEvalContexts.back().isPotentiallyEvaluatedIfUsed(); + } + /// RAII class used to determine whether SFINAE has /// trapped any errors that occur during template argument /// deduction. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7661,8 +7661,16 @@ return std::move(Err); } + Expr* RewrittenArg = nullptr; + if (E->hasRewrittenArg()) { + auto ArgOrErr = import(E->getExpr()); + if (!ArgOrErr) return ArgOrErr.takeError(); + RewrittenArg = *ArgOrErr; + } + return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr, - *ToParamOrErr, *UsedContextOrErr); + *ToParamOrErr, *UsedContextOrErr, + RewrittenArg); } ExpectedStmt diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2168,20 +2168,27 @@ llvm_unreachable("unexpected IdentKind!"); } +SourceLocExpr::Context +SourceLocExpr::Context::FromDefaultArg(const CXXDefaultArgExpr *E) { + return {E->getUsedLocation(), E->getUsedContext()}; +} + +SourceLocExpr::Context +SourceLocExpr::Context::FromDefaultInit(const CXXDefaultInitExpr *E) { + return {E->getUsedLocation(), E->getUsedContext()}; +} + APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, - const Expr *DefaultExpr) const { + llvm::Optional LocCtx) const { SourceLocation Loc; const DeclContext *Context; - - std::tie(Loc, - Context) = [&]() -> std::pair { - if (auto *DIE = dyn_cast_or_null(DefaultExpr)) - return {DIE->getUsedLocation(), DIE->getUsedContext()}; - if (auto *DAE = dyn_cast_or_null(DefaultExpr)) - return {DAE->getUsedLocation(), DAE->getUsedContext()}; - return {this->getLocation(), this->getParentContext()}; - }(); - + if (LocCtx) { + Loc = LocCtx->UsedLocation; + Context = LocCtx->UsedContext; + } else { + Loc = this->getLocation(); + Context = this->getParentContext(); + } PresumedLoc PLoc = Ctx.getSourceManager().getPresumedLoc( Ctx.getSourceManager().getExpansionRange(Loc).getEnd()); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7448,7 +7448,8 @@ { return StmtVisitorTy::Visit(E->getReplacement()); } bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { TempVersionRAII RAII(*Info.CurrentCall); - SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope); + SourceLocExprScopeGuard Guard(SourceLocExpr::Context::FromDefaultArg(E), + Info.CurrentCall->CurSourceLocExprScope); return StmtVisitorTy::Visit(E->getExpr()); } bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { @@ -7456,7 +7457,8 @@ // The initializer may not have been parsed yet, or might be erroneous. if (!E->getExpr()) return Error(E); - SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope); + SourceLocExprScopeGuard Guard(SourceLocExpr::Context::FromDefaultInit(E), + Info.CurrentCall->CurSourceLocExprScope); return StmtVisitorTy::Visit(E->getExpr()); } @@ -8771,7 +8773,7 @@ bool VisitSourceLocExpr(const SourceLocExpr *E) { assert(!E->isIntType() && "SourceLocExpr isn't a pointer type?"); APValue LValResult = E->EvaluateInContext( - Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr()); + Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getSLocs()); Result.setFrom(Info.Ctx, LValResult); return true; } @@ -11122,7 +11124,7 @@ bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) { APValue Evaluated = E->EvaluateInContext( - Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr()); + Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getSLocs()); return Success(Evaluated, E); } @@ -15146,14 +15148,18 @@ return true; } -bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, - ConstantExprKind Kind) const { +bool Expr::EvaluateAsConstantExpr( + EvalResult &Result, const ASTContext &Ctx, ConstantExprKind Kind, + CurrentSourceLocExprScope *SLocs) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; + if (SLocs) { + Info.BottomFrame.CurSourceLocExprScope = *SLocs; + } // The type of the object we're initializing is 'const T' for a class NTTP. QualType T = getType(); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/LocInfoType.h" #include "clang/AST/Type.h" #include "clang/Basic/Module.h" @@ -999,6 +1000,10 @@ [=] { Visit(Node->getAPValueResult(), Node->getType()); }); } +void TextNodeDumper::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* Node) { + AddChild("expr", [=] { Node->getExpr()->dumpColor(); }); +} + void TextNodeDumper::VisitCallExpr(const CallExpr *Node) { if (Node->usesADL()) OS << " adl"; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -646,7 +646,7 @@ Value *VisitSourceLocExpr(SourceLocExpr *SLE) { auto &Ctx = CGF.getContext(); APValue Evaluated = - SLE->EvaluateInContext(Ctx, CGF.CurSourceLocExprScope.getDefaultExpr()); + SLE->EvaluateInContext(Ctx, CGF.CurSourceLocExprScope.getSLocs()); return ConstantEmitter(CGF).emitAbstract(SLE->getLocation(), Evaluated, SLE->getType()); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -23,6 +23,7 @@ #include "VarBypassDetector.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -1622,7 +1623,8 @@ CXXDefaultInitExprScope(CodeGenFunction &CGF, const CXXDefaultInitExpr *E) : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue), OldCXXThisAlignment(CGF.CXXThisAlignment), - SourceLocScope(E, CGF.CurSourceLocExprScope) { + SourceLocScope(SourceLocExpr::Context::FromDefaultInit(E), + CGF.CurSourceLocExprScope) { CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer(); CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment(); } @@ -1640,7 +1642,8 @@ struct CXXDefaultArgExprScope : SourceLocExprScopeGuard { CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) - : SourceLocExprScopeGuard(E, CGF.CurSourceLocExprScope) {} + : SourceLocExprScopeGuard(SourceLocExpr::Context::FromDefaultArg(E), + CGF.CurSourceLocExprScope) {} }; /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6142,7 +6142,8 @@ // that we can properly codegen the constructor closure. if (!Class->isDependentContext()) { for (ParmVarDecl *PD : CD->parameters()) { - (void)S.CheckCXXDefaultArgExpr(Attr->getLocation(), CD, PD); + Expr* Dummy; + (void)S.CheckCXXDefaultArgExpr(Attr->getLocation(), CD, PD, Dummy); S.DiscardCleanupsInEvaluationContext(); } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17,6 +17,9 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/CurrentSourceLocExprScope.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -44,9 +47,11 @@ #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" #include "clang/Sema/SemaFixItUtils.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" @@ -5848,8 +5853,30 @@ ArraySubscriptExpr(LHSExp, RHSExp, ResultType, VK, OK, RLoc); } +static bool IsStdSourceLocationCurrent(Decl* D) { + auto *F = D ? dyn_cast(D) : nullptr; + auto *FuncName = F ? F->getIdentifier() : nullptr; + if (!FuncName || !FuncName->isStr("current") || !F->isConsteval()) + return false; + auto *Cls = dyn_cast(F->getDeclContext()); + auto *ClsName = Cls ? Cls->getIdentifier() : nullptr; + return ClsName && ClsName->isStr("source_location") && Cls->isInStdNamespace(); +} + +static bool IsCallToStdSourceLocationCurrent(Expr* E) { + CallExpr *Call = dyn_cast(E); + auto *Callee = Call ? Call->getCalleeDecl() : nullptr; + return Callee && IsStdSourceLocationCurrent(Callee); +} + +static ExprResult EvaluateSourceLocations(Sema &S, ParmVarDecl *Param, + SourceLocation CallLoc, + DeclContext *UsedContext); + bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, - ParmVarDecl *Param) { + ParmVarDecl *Param, Expr *&RewrittenArg) { + RewrittenArg = nullptr; + if (Param->hasUnparsedDefaultArg()) { // If we've already cleared out the location for the default argument, // that means we're parsing it right now. @@ -5873,6 +5900,12 @@ assert(Param->hasInit() && "default argument but no initializer?"); + ExprResult NewArg = EvaluateSourceLocations(*this, Param, CallLoc, CurContext); + if (NewArg.isInvalid()) + return true; + if (NewArg.get() != Param->getDefaultArg()) + RewrittenArg = NewArg.get(); + // If the default expression creates temporaries, we need to // push them to the current stack of expression temporaries so they'll // be properly destroyed. @@ -5905,9 +5938,11 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); - if (CheckCXXDefaultArgExpr(CallLoc, FD, Param)) + Expr* RewrittenArg = nullptr; + if (CheckCXXDefaultArgExpr(CallLoc, FD, Param, RewrittenArg)) return ExprError(); - return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext); + return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext, + RewrittenArg); } Sema::VariadicCallType @@ -17519,13 +17554,20 @@ } static void EvaluateAndDiagnoseImmediateInvocation( - Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate) { + Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate, bool InDefaultArg) { llvm::SmallVector Notes; Expr::EvalResult Eval; Eval.Diag = &Notes; ConstantExpr *CE = Candidate.getPointer(); + // [support.srcloc.cons]p2. + // Any call to current that appears as a default argument ([dcl.fct.default]), + // or as a subexpression thereof, should correspond to the location of the + // invocation of the function that uses the default argument ([expr.call]). + if (InDefaultArg && IsCallToStdSourceLocationCurrent(CE->getSubExpr())) + return; // Postpone evaluation until we see the context. bool Result = CE->EvaluateAsConstantExpr( - Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); + Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation, + SemaRef.OverridenSourceLocs); if (!Result || !Notes.empty()) { Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); if (auto *FunctionalCast = dyn_cast(InnerExpr)) @@ -17546,6 +17588,51 @@ CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); } +static ExprResult EvaluateSourceLocations(Sema &S, ParmVarDecl* Param, + SourceLocation CallLoc, + DeclContext *UsedContext) { + struct Transform : TreeTransform { + using TreeTransform::TreeTransform; + + bool AlwaysRebuild() { return OverrideAlwaysRebuild; } + + ExprResult TransformCallExpr(CallExpr* Call) { + if (!IsCallToStdSourceLocationCurrent(Call)) + return TreeTransform::TransformCallExpr(Call); + // Rebuild the call so it gets to the list of immedicate invocations. + llvm::SaveAndRestore Guard(OverrideAlwaysRebuild, true); + return TreeTransform::TransformCallExpr(Call); + } + + private: + bool OverrideAlwaysRebuild = false; + }; + // Force evaluation of calls to source_location::current(). + // TODO(HACK): potentially evaluated is clearly wrong in some cases? + CurrentSourceLocExprScope Locs; + CurrentSourceLocExprScope::SourceLocExprScopeGuard LG( + SourceLocExpr::Context{CallLoc, UsedContext}, Locs); + llvm::SaveAndRestore SwapLocs( + S.OverridenSourceLocs, &Locs); + + EnterExpressionEvaluationContext Eval( + S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + Sema::ReuseLambdaContextDecl_t{}); + ExprResult NewArg = Transform(S).TransformExpr(Param->getDefaultArg()); + if (NewArg.isUsable() && NewArg.get() != Param->getDefaultArg()) { + NewArg = S.ConvertParamDefaultArgument(Param, NewArg.get(), + NewArg.get()->getBeginLoc()); + } + if (NewArg.isInvalid()) + return NewArg; + // Mimic the logic of ParmVarDecl::getDefaultArg(). + if (auto *E = dyn_cast_or_null(NewArg.get())) { + if (!isa(E)) + return E->getSubExpr(); + } + return NewArg; +} + static void RemoveNestedImmediateInvocation( Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, SmallVector::reverse_iterator It) { @@ -17664,7 +17751,8 @@ } for (auto CE : Rec.ImmediateInvocationCandidates) if (!CE.getInt()) - EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + EvaluateAndDiagnoseImmediateInvocation( + SemaRef, CE, /*InDefaultArg=*/Rec.isPotentiallyEvaluatedIfUsed()); for (auto DR : Rec.ReferenceToConsteval) { auto *FD = cast(DR->getDecl()); SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1051,7 +1051,8 @@ // We don't keep the instantiated default argument expressions around so // we must rebuild them here. for (unsigned I = 1, E = CD->getNumParams(); I != E; ++I) { - if (CheckCXXDefaultArgExpr(ThrowLoc, CD, CD->getParamDecl(I))) + Expr* Dummy; + if (CheckCXXDefaultArgExpr(ThrowLoc, CD, CD->getParamDecl(I), Dummy)) return true; } } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -847,8 +847,9 @@ if (!Attr) return; for (unsigned I = 0; I != NumParams; ++I) { + Expr *Dummy; (void)CheckCXXDefaultArgExpr(Attr->getLocation(), Ctor, - Ctor->getParamDecl(I)); + Ctor->getParamDecl(I), Dummy); CleanupVarDeclMarking(); } } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1827,6 +1827,9 @@ E->Param = readDeclAs(); E->UsedContext = readDeclAs(); E->CXXDefaultArgExprBits.Loc = readSourceLocation(); + E->CXXDefaultArgExprBits.HasRewrittenArg = Record.readInt(); + if (E->CXXDefaultArgExprBits.HasRewrittenArg) + *E->getTrailingObjects() = Record.readSubExpr(); } void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { @@ -3817,7 +3820,9 @@ break; case EXPR_CXX_DEFAULT_ARG: - S = new (Context) CXXDefaultArgExpr(Empty); + S = CXXDefaultArgExpr::CreateEmpty( + Context, + /*HasRewrittenArg*/ Record[ASTStmtReader::NumExprFields]); break; case EXPR_CXX_DEFAULT_INIT: diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1747,6 +1747,9 @@ Record.AddDeclRef(E->getParam()); Record.AddDeclRef(cast_or_null(E->getUsedContext())); Record.AddSourceLocation(E->getUsedLocation()); + Record.push_back(E->hasRewrittenArg()); + if (E->hasRewrittenArg()) + Record.AddStmt(E->getExpr()); Code = serialization::EXPR_CXX_DEFAULT_ARG; } diff --git a/clang/test/CodeGenCXX/builtin-source-location.cpp b/clang/test/CodeGenCXX/builtin-source-location.cpp --- a/clang/test/CodeGenCXX/builtin-source-location.cpp +++ b/clang/test/CodeGenCXX/builtin-source-location.cpp @@ -13,12 +13,12 @@ namespace std { class source_location { public: - static constexpr source_location current(const void *__p = __builtin_source_location()) noexcept { + static consteval source_location current(const void *__p = __builtin_source_location()) noexcept { source_location __loc; __loc.__m_impl = static_cast(__p); return __loc; } - static source_location bad_current(const void *__p = __builtin_source_location()) noexcept { + static constexpr source_location bad_current(const void *__p = __builtin_source_location()) noexcept { return current(__p); } constexpr source_location() = default; diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp --- a/clang/test/SemaCXX/source_location.cpp +++ b/clang/test/SemaCXX/source_location.cpp @@ -364,8 +364,8 @@ template void func_template_tests() { constexpr auto P = test_func_template(42); - //static_assert(is_equal(P.first.function(), __func__), ""); - //static_assert(!is_equal(P.second.function(), __func__), ""); + static_assert(is_equal(P.first.function(), __PRETTY_FUNCTION__), ""); + static_assert(!is_equal(P.second.function(), __PRETTY_FUNCTION__), ""); } template void func_template_tests(); diff --git a/clang/test/SemaCXX/source_location_consteval.cpp b/clang/test/SemaCXX/source_location_consteval.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/source_location_consteval.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// expected-no-diagnostics + +namespace std { +class source_location { + struct __impl; + +public: + static consteval source_location current(const __impl *__p = __builtin_source_location()) noexcept { + source_location __loc; + __loc.__m_impl = __p; + return __loc; + } + constexpr source_location() = default; + constexpr source_location(source_location const &) = default; + constexpr unsigned int line() const noexcept { return __m_impl ? __m_impl->_M_line : 0; } + constexpr unsigned int column() const noexcept { return __m_impl ? __m_impl->_M_column : 0; } + constexpr const char *file() const noexcept { return __m_impl ? __m_impl->_M_file_name : ""; } + constexpr const char *function() const noexcept { return __m_impl ? __m_impl->_M_function_name : ""; } + +private: + // Note: The type name "std::source_location::__impl", and its constituent + // field-names are required by __builtin_source_location(). + struct __impl { + const char *_M_file_name; + const char *_M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl *__m_impl = nullptr; + +public: + using public_impl_alias = __impl; +}; +} // namespace std + +using SL = std::source_location; + +constexpr bool is_equal(const char *LHS, const char *RHS) { + while (*LHS != 0 && *RHS != 0) { + if (*LHS != *RHS) + return false; + ++LHS; + ++RHS; + } + return *LHS == 0 && *RHS == 0; +} + +constexpr SL get_sl(SL l = SL::current()) { return l; } +SL get_sl_not_const(SL l = SL::current()) { return l; } + +#line 700 "CheckDefaultArg.h" +constexpr SL l = get_sl(); +static_assert(l.line() == 700); +static_assert(is_equal(l.file(), "CheckDefaultArg.h")); + +int test() { + static_assert(is_equal(get_sl().function(), __PRETTY_FUNCTION__)); + static_assert(get_sl().line() == __LINE__); + return get_sl().line() + get_sl_not_const().line(); +}