cor3ntin created this revision.
Herald added a subscriber: martong.
Herald added a reviewer: shafik.
Herald added a project: All.
cor3ntin requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D136554

Files:
  clang/include/clang/AST/ExprCXX.h
  clang/include/clang/AST/Stmt.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTImporter.cpp
  clang/lib/AST/ExprCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTReaderStmt.cpp
  clang/lib/Serialization/ASTWriterStmt.cpp

Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1748,6 +1748,9 @@
   Record.AddDeclRef(E->getParam());
   Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
   Record.AddSourceLocation(E->getUsedLocation());
+  Record.push_back(E->hasRewrittenInit());
+  if (E->hasRewrittenInit())
+    Record.AddStmt(E->getRewrittenExpr());
   Code = serialization::EXPR_CXX_DEFAULT_ARG;
 }
 
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1828,6 +1828,9 @@
   E->Param = readDeclAs<ParmVarDecl>();
   E->UsedContext = readDeclAs<DeclContext>();
   E->CXXDefaultArgExprBits.Loc = readSourceLocation();
+  E->CXXDefaultArgExprBits.HasRewrittenInit = Record.readInt();
+  if(E->CXXDefaultArgExprBits.HasRewrittenInit)
+     *E->getTrailingObjects<Expr *>() = 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:
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ 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<typename Derived>
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ 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<FunctionDecl>(E->getParam()->getDeclContext()),
-                                        E->getParam());
+                           cast<FunctionDecl>(E->getParam()->getDeclContext()), E->getParam());
 }
 
 template<typename Fn>
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ 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<ExprWithCleanups>(Param->getInit())) {
+  if (auto InitWithCleanup = dyn_cast<ExprWithCleanups>(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<bool> DisableIITracking(
+      CheckingDefaultArgument, true);
+
   EnterExpressionEvaluationContext EvalContext(
       *this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);
-  MarkDeclarationsReferencedInExpr(Param->getDefaultArg(),
-                                   /*SkipLocalVariables=*/true);
+  MarkDeclarationsReferencedInExpr(Init, true);
   return false;
 }
 
+
+struct ImmediateCallVisitor : public RecursiveASTVisitor<ImmediateCallVisitor> {
+  bool HasImmediateCalls = false;
+  bool VisitCallExpr(CallExpr *E) {
+    if(const FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl())) {
+      HasImmediateCalls = HasImmediateCalls || FD->isConsteval();
+    }
+    return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+  }
+
+  bool VisitSourceLocExpr(SourceLocExpr *E) {
+    HasImmediateCalls = true;
+    return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+  }
+};
+
+struct EnsureImmediateInvocationInDefaultArgs : TreeTransform<EnsureImmediateInvocationInDefaultArgs> {
+  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<bool> 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<bool> 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<FunctionDecl>(DR->getDecl());
-    SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address)
-        << FD;
-    SemaRef.Diag(FD->getLocation(), diag::note_declared_at);
+      auto *FD = cast<FunctionDecl>(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<FunctionDecl>(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,
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -949,6 +949,52 @@
   return cast<FunctionDecl>(getCalleeDecl())->getLiteralIdentifier();
 }
 
+
+CXXDefaultArgExpr *CXXDefaultArgExpr::CreateEmpty(const ASTContext &C, bool HasRewrittenInit) {
+  size_t Size = totalSizeToAlloc<Expr*>(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<Expr*>(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<FullExpr>(Init))
+    if (!isa<ConstantExpr>(E))
+      return E->getSubExpr();
+  return Init;
+}
+
+Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() {
+  if(!hasRewrittenInit())
+    return nullptr;
+  Expr* Init = getRewrittenExpr();
+  if (auto *E = dyn_cast_or_null<FullExpr>(Init))
+    if (!isa<ConstantExpr>(E))
+      return E->getSubExpr();
+  return Init;
+}
+
 CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx,
                                        SourceLocation Loc, FieldDecl *Field,
                                        QualType Ty, DeclContext *UsedContext)
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ 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
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ 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.
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ 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;
   };
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ 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<CXXDefaultArgExpr, Expr *> {
   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<Expr *>() = 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<Expr *>() : nullptr;
+  }
+  Expr *getRewrittenExpr() {
+       return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
+  }
+
+  const Expr *getAdjustedRewrittenExpr() const;
+  Expr *getAdjustedRewrittenExpr();
 
   const DeclContext *getUsedContext() const { return UsedContext; }
   DeclContext *getUsedContext() { return UsedContext; }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to