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 @@ -1245,8 +1245,12 @@ /// 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 class ASTReader; + friend TrailingObjects; /// The parameter whose default is being used. ParmVarDecl *Param; @@ -1254,7 +1258,9 @@ /// The context where the default argument expression was used. DeclContext *UsedContext; - CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param, + CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, + ParmVarDecl *Param, + Expr* InitExpr, DeclContext *UsedContext) : Expr(SC, Param->hasUnparsedDefaultArg() @@ -1262,30 +1268,52 @@ : Param->getDefaultArg()->getType(), Param->getDefaultArg()->getValueKind(), Param->getDefaultArg()->getObjectKind()), - Param(Param), UsedContext(UsedContext) { + Param(Param), + UsedContext(UsedContext) { CXXDefaultArgExprBits.Loc = Loc; + CXXDefaultArgExprBits.HasRewrittenInit = InitExpr != nullptr; + if (InitExpr) + *getTrailingObjects() = InitExpr; setDependence(computeDependence(this)); } + CXXDefaultArgExpr(EmptyShell Empty, bool HasRewrittenInit) + : Expr(CXXDefaultArgExprClass, Empty) { + CXXDefaultArgExprBits.HasRewrittenInit = HasRewrittenInit; + } + + size_t numTrailingObjects() const { + return CXXDefaultArgExprBits.HasRewrittenInit; + } + public: - CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {} + static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C, bool HasRewrittenInit); // \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); - } - + Expr* RewrittenExpr, + DeclContext *UsedContext); // 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(); } + bool hasRewrittenInit() const { return CXXDefaultArgExprBits.HasRewrittenInit; } + + // Retrieve the argument to the function call. + const Expr *getExpr() const; + Expr *getExpr(); + + const Expr *getRewrittenExpr() const { + return hasRewrittenInit() ? *getTrailingObjects() : nullptr; + } + Expr *getRewrittenExpr() { + return hasRewrittenInit() ? *getTrailingObjects() : nullptr; + } + + const Expr *getAdjustedRewrittenExpr() const; + Expr *getAdjustedRewrittenExpr(); 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,10 @@ unsigned : NumExprBits; + + /// Whether this CXXDefaultArgExpr rewrote its argument and stores a copy. + unsigned HasRewrittenInit : 1; + /// The location where the default argument expression was used. SourceLocation Loc; }; 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 @@ -1037,6 +1037,14 @@ /// functions aren't tracked when this is set. bool RebuildingImmediateInvocation = false; + /// Whether we are currently checking a default argument + /// This disables immediate invocation checking + bool CheckingDefaultArgument = false; + + /// Whether we are currently rebuilding a default argument + /// This disable capture check in lambdas + bool RebuildingDefaultArgument = false; + /// Used to change context to isConstantEvaluated without pushing a heavy /// ExpressionEvaluationContextRecord object. bool isConstantEvaluatedOverride; @@ -6201,13 +6209,13 @@ /// 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 *Init = nullptr); /// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating /// the default expr if needed. ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, - ParmVarDecl *Param); + ParmVarDecl *Param, Expr *Init = nullptr); /// FinalizeVarWithDestructor - Prepare for calling destructor on the /// constructed variable. @@ -9573,6 +9581,20 @@ return ExprEvalContexts.back().isImmediateFunctionContext(); } + bool isDefaultArgumentContext() const { + if(CheckingDefaultArgument) + return true; + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + for(const ExpressionEvaluationContextRecord & Ctx : llvm::reverse(ExprEvalContexts)) { + if(Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) + return true; + if(Ctx.isUnevaluated()) + return false; + } + return false; + } + /// 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 @@ -7691,9 +7691,15 @@ if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam)) return std::move(Err); } - + Expr* RewrittenInit = nullptr; + if(E->hasRewrittenInit()) { + ExpectedExpr ExprOrErr = import(E->getExpr()); + if(!ExprOrErr) + return ExprOrErr.takeError(); + RewrittenInit = ExprOrErr.get(); + } return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr, - *ToParamOrErr, *UsedContextOrErr); + *ToParamOrErr, RewrittenInit, *UsedContextOrErr); } ExpectedStmt diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -949,6 +949,52 @@ return cast(getCalleeDecl())->getLiteralIdentifier(); } + +CXXDefaultArgExpr *CXXDefaultArgExpr::CreateEmpty(const ASTContext &C, bool HasRewrittenInit) { + size_t Size = totalSizeToAlloc(HasRewrittenInit); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(EmptyShell(), HasRewrittenInit); +} + +CXXDefaultArgExpr *CXXDefaultArgExpr::Create(const ASTContext &C, SourceLocation Loc, + ParmVarDecl *Param, + Expr* RewrittenExpr, + DeclContext *UsedContext) { + size_t Size = totalSizeToAlloc(RewrittenExpr != nullptr ? 1 : 0); + auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr)); + return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, RewrittenExpr, + UsedContext); +} + +const Expr *CXXDefaultArgExpr::getExpr() const { + return CXXDefaultArgExprBits.HasRewrittenInit? getAdjustedRewrittenExpr() : getParam()->getDefaultArg(); +} + +Expr *CXXDefaultArgExpr::getExpr() { + return CXXDefaultArgExprBits.HasRewrittenInit? getAdjustedRewrittenExpr() : getParam()->getDefaultArg(); +} + +const Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() const { + if(!hasRewrittenInit()) + return nullptr; + const Expr* Init = getRewrittenExpr(); + + if (auto *E = dyn_cast_or_null(Init)) + if (!isa(E)) + return E->getSubExpr(); + return Init; +} + +Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() { + if(!hasRewrittenInit()) + return nullptr; + Expr* Init = getRewrittenExpr(); + if (auto *E = dyn_cast_or_null(Init)) + if (!isa(E)) + return E->getSubExpr(); + return Init; +} + CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc, FieldDecl *Field, QualType Ty, DeclContext *UsedContext) 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 @@ -5856,8 +5856,9 @@ } bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, - ParmVarDecl *Param) { + ParmVarDecl *Param, Expr* Init) { if (Param->hasUnparsedDefaultArg()) { + assert(!Init && "Should not have an init expression yet"); // If we've already cleared out the location for the default argument, // that means we're parsing it right now. if (!UnparsedDefaultArgLocs.count(Param)) { @@ -5874,11 +5875,16 @@ return true; } - if (Param->hasUninstantiatedDefaultArg() && - InstantiateDefaultArgument(CallLoc, FD, Param)) - return true; + if (Param->hasUninstantiatedDefaultArg()) { + assert(!Init && "Should not have an init expression yet"); + if(InstantiateDefaultArgument(CallLoc, FD, Param)) + return true; + } + if(!Init) { + Init = Param->getInit(); + } - assert(Param->hasInit() && "default argument but no initializer?"); + assert(Init && "default argument but no initializer?"); // If the default expression creates temporaries, we need to // push them to the current stack of expression temporaries so they'll @@ -5887,34 +5893,73 @@ // bound temporaries; see the comment in PR5810. // We don't need to do that with block decls, though, because // blocks in default argument expression can never capture anything. - if (auto Init = dyn_cast(Param->getInit())) { + if (auto InitWithCleanup = dyn_cast(Init)) { // Set the "needs cleanups" bit regardless of whether there are // any explicit objects. - Cleanup.setExprNeedsCleanups(Init->cleanupsHaveSideEffects()); - + Cleanup.setExprNeedsCleanups(InitWithCleanup->cleanupsHaveSideEffects()); // Append all the objects to the cleanup list. Right now, this // should always be a no-op, because blocks in default argument // expressions should never be able to capture anything. - assert(!Init->getNumObjects() && + assert(!InitWithCleanup->getNumObjects() && "default argument expression has capturing blocks?"); } - // We already type-checked the argument, so we know it works. - // Just mark all of the declarations in this potentially-evaluated expression - // as being "referenced". + /// Do not try to check immediate invocations when checking defaults arguments + llvm::SaveAndRestore DisableIITracking( + CheckingDefaultArgument, true); + EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Param); - MarkDeclarationsReferencedInExpr(Param->getDefaultArg(), - /*SkipLocalVariables=*/true); + MarkDeclarationsReferencedInExpr(Init, true); return false; } + +struct ImmediateCallVisitor : public RecursiveASTVisitor { + bool HasImmediateCalls = false; + bool VisitCallExpr(CallExpr *E) { + if(const FunctionDecl* FD = dyn_cast_or_null(E->getCalleeDecl())) { + HasImmediateCalls = HasImmediateCalls || FD->isConsteval(); + } + return RecursiveASTVisitor::VisitStmt(E); + } + + bool VisitSourceLocExpr(SourceLocExpr *E) { + HasImmediateCalls = true; + return RecursiveASTVisitor::VisitStmt(E); + } +}; + +struct EnsureImmediateInvocationInDefaultArgs : TreeTransform { + EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef) : TreeTransform(SemaRef) {} +}; + ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, - FunctionDecl *FD, ParmVarDecl *Param) { + FunctionDecl *FD, ParmVarDecl *Param, Expr* Init) { assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); - if (CheckCXXDefaultArgExpr(CallLoc, FD, Param)) + + if(!Init && !Param->hasUnparsedDefaultArg() && !Param->hasUninstantiatedDefaultArg()) { + ImmediateCallVisitor V; + V.TraverseDecl(Param); + if(V.HasImmediateCalls) { + EnterExpressionEvaluationContext EvalContext( + *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Param); + EnsureImmediateInvocationInDefaultArgs Immediate(*this); + llvm::SaveAndRestore RebuildRAII(RebuildingDefaultArgument, true); + ExprResult Res = Immediate.TransformExpr(Param->getInit()); + if(Res.isInvalid()) + return ExprError(); + Res = ConvertParamDefaultArgument(Param, Res.get(), Res.get()->getBeginLoc()); + if(Res.isInvalid()) + return ExprError(); + Init = Res.get(); + } + } + llvm::SaveAndRestore DisableIITracking(CheckingDefaultArgument, true); + if (CheckCXXDefaultArgExpr(CallLoc, FD, Param, Init)) return ExprError(); - return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext); + + return CXXDefaultArgExpr::Create(Context, CallLoc, Param, Init, CurContext); } Sema::VariadicCallType @@ -17538,7 +17583,7 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { if (isUnevaluatedContext() || !E.isUsable() || !Decl || - !Decl->isConsteval() || isConstantEvaluated() || + !Decl->isConsteval() || isConstantEvaluated() || isDefaultArgumentContext() || RebuildingImmediateInvocation || isImmediateFunctionContext()) return E; @@ -17691,7 +17736,7 @@ /// When we have more then 1 ImmediateInvocationCandidates we need to check /// for nested ImmediateInvocationCandidates. when we have only 1 we only /// need to remove ReferenceToConsteval in the immediate invocation. - if (Rec.ImmediateInvocationCandidates.size() > 1) { + if (Rec.ImmediateInvocationCandidates.size() > 1) { /// Prevent sema calls during the tree transform from adding pointers that /// are already in the sets. @@ -17722,10 +17767,10 @@ if (!CE.getInt()) EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); for (auto *DR : Rec.ReferenceToConsteval) { - auto *FD = cast(DR->getDecl()); - SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) - << FD; - SemaRef.Diag(FD->getLocation(), diag::note_declared_at); + auto *FD = cast(DR->getDecl()); + SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) + << FD; + SemaRef.Diag(FD->getLocation(), diag::note_declared_at); } } @@ -18818,11 +18863,13 @@ } } - // If the variable is declared in the current context, there is no need to // capture it. if (VarDC == DC) return true; + if(RebuildingDefaultArgument) + return true; + // Capture global variables if it is required to use private copy of this // variable. bool IsGlobal = !VD->hasLocalStorage(); @@ -19731,7 +19778,7 @@ if (auto *FD = dyn_cast(E->getDecl())) if (!isUnevaluatedContext() && !isConstantEvaluated() && - !isImmediateFunctionContext() && FD->isConsteval() && + !isImmediateFunctionContext() && !isDefaultArgumentContext() && FD->isConsteval() && !RebuildingImmediateInvocation && !FD->isDependentContext()) ExprEvalContexts.back().ReferenceToConsteval.insert(E); MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1928,8 +1928,7 @@ getDescribedFunctionTemplate() && "Default arg expressions are never formed in dependent cases."); return SemaRef.BuildCXXDefaultArgExpr(E->getUsedLocation(), - cast(E->getParam()->getDeclContext()), - E->getParam()); + cast(E->getParam()->getDeclContext()), E->getParam()); } template diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3164,8 +3164,8 @@ /// By default, builds a new default-argument expression, which does not /// require any semantic analysis. Subclasses may override this routine to /// provide different behavior. - ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) { - return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param, + ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param, Expr* RewrittenExpr) { + return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param, RewrittenExpr, getSema().CurContext); } @@ -12052,11 +12052,18 @@ if (!Param) return ExprError(); + ExprResult InitRes; + if(E->hasRewrittenInit()) { + InitRes = getDerived().TransformExpr(E->getRewrittenExpr()); + if (InitRes.isInvalid()) + return ExprError(); + } + if (!getDerived().AlwaysRebuild() && Param == E->getParam() && - E->getUsedContext() == SemaRef.CurContext) + E->getUsedContext() == SemaRef.CurContext && InitRes.get() == E->getRewrittenExpr()) return E; - return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param); + return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param, InitRes.get()); } template 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 @@ -1828,6 +1828,9 @@ E->Param = readDeclAs(); E->UsedContext = readDeclAs(); E->CXXDefaultArgExprBits.Loc = readSourceLocation(); + E->CXXDefaultArgExprBits.HasRewrittenInit = Record.readInt(); + if(E->CXXDefaultArgExprBits.HasRewrittenInit) + *E->getTrailingObjects() = Record.readSubExpr(); } void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { @@ -3821,7 +3824,7 @@ break; case EXPR_CXX_DEFAULT_ARG: - S = new (Context) CXXDefaultArgExpr(Empty); + S = CXXDefaultArgExpr::CreateEmpty(Context, /*HasRewrittenInit*/ 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 @@ -1748,6 +1748,9 @@ Record.AddDeclRef(E->getParam()); Record.AddDeclRef(cast_or_null(E->getUsedContext())); Record.AddSourceLocation(E->getUsedLocation()); + Record.push_back(E->hasRewrittenInit()); + if (E->hasRewrittenInit()) + Record.AddStmt(E->getRewrittenExpr()); Code = serialization::EXPR_CXX_DEFAULT_ARG; }