sepavloff created this revision.

While a function body is being parsed, the function declaration is not 
considered
as a definition because it does not have a body yet. In some cases it leads to
incorrect interpretation, the case is presented in
https://bugs.llvm.org/show_bug.cgi?id=14785:

      template<typename T> struct Somewhat {
        void internal() const {}
        friend void operator+(int const &, Somewhat<T> const &) {}
      };
  void operator+(int const &, Somewhat<char> const &x) { x.internal(); }

When statement `x.internal()` in the body of global `operator+` is parsed, the 
type
of `x` must be completed, so the instantiation of `Somewhat<char>` is started. 
It
instantiates the declaration of `operator+` defined inline, and makes a check 
for
redefinition. The check does not detect another definition because the 
declaration
of `operator+` is still not defining as does not have a body yet.

This change solves this problem by using flag `WillHaveBody`. It introduces new
semantic action `ActOnStartFunctionBody`, which set this flag so that 
definitions
that do not have body (such as deleted functions) do not have this flag.

This change fixes PR14785.

The fix requires https://reviews.llvm.org/D26065 be applied otherwise 
diagnostics is awful.


https://reviews.llvm.org/D30375

Files:
  include/clang/AST/Decl.h
  include/clang/Sema/Sema.h
  lib/AST/Decl.cpp
  lib/Parse/Parser.cpp
  lib/Sema/SemaCUDA.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/SemaCXX/friend2.cpp

Index: test/SemaCXX/friend2.cpp
===================================================================
--- test/SemaCXX/friend2.cpp
+++ test/SemaCXX/friend2.cpp
@@ -170,3 +170,15 @@
 template class Test<int>;
 
 }
+
+namespace pr14785 {
+template<typename T>
+struct Somewhat {
+  void internal() const { }
+  friend void operator+(int const &, Somewhat<T> const &) {}  // expected-error{{redefinition of 'operator+'}}
+};
+
+void operator+(int const &, Somewhat<char> const &x) {  // expected-note {{previous definition is here}}
+  x.internal();  // expected-note{{in instantiation of template class 'pr14785::Somewhat<char>' requested here}}
+}
+}
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3832,6 +3832,7 @@
     SubstQualifier(*this, PatternDecl, Function, TemplateArgs);
 
     ActOnStartOfFunctionDef(nullptr, Function);
+    ActOnStartFunctionBody(Function);
 
     // Enter the scope of this instantiation. We don't use
     // PushDeclContext because we don't have a scope.
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -11804,11 +11804,6 @@
       return D;
   }
 
-  // Mark this function as "will have a body eventually".  This lets users to
-  // call e.g. isInlineDefinitionExternallyVisible while we're still parsing
-  // this function.
-  FD->setWillHaveBody();
-
   // If we are instantiating a generic lambda call operator, push
   // a LambdaScopeInfo onto the function stack.  But use the information
   // that's already been calculated (ActOnLambdaExpr) to prime the current
@@ -11978,6 +11973,19 @@
   return Decl;
 }
 
+/// Semantic action called by parser when it expects that the current function
+/// definition will have a body statement.
+void Sema::ActOnStartFunctionBody(Decl *D) {
+  if (!D)
+    return;
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    // Mark this function as "will have a body eventually".  This lets users to
+    // call e.g. isInlineDefinitionExternallyVisible while we're still parsing
+    // this function.
+    FD->setWillHaveBody();
+  }
+}
+
 Decl *Sema::ActOnFinishFunctionBody(Decl *D, Stmt *BodyArg) {
   return ActOnFinishFunctionBody(D, BodyArg, false);
 }
Index: lib/Sema/SemaCUDA.cpp
===================================================================
--- lib/Sema/SemaCUDA.cpp
+++ lib/Sema/SemaCUDA.cpp
@@ -629,12 +629,6 @@
   // emitted, because (say) the definition could include "inline".
   FunctionDecl *Def = FD->getDefinition();
 
-  // We may currently be parsing the body of FD, in which case
-  // FD->getDefinition() will be null, but we still want to treat FD as though
-  // it's a definition.
-  if (!Def && FD->willHaveBody())
-    Def = FD;
-
   if (Def &&
       !isDiscardableGVALinkage(S.getASTContext().GetGVALinkageForFunction(Def)))
     return true;
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -1194,6 +1194,8 @@
     return Actions.ActOnFinishFunctionBody(Res, nullptr, false);
   }
 
+  Actions.ActOnStartFunctionBody(Res);
+
   if (Tok.is(tok::kw_try))
     return ParseFunctionTryBlock(Res, BodyScope);
 
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -2529,7 +2529,7 @@
 bool FunctionDecl::isDefined(const FunctionDecl *&Definition) const {
   for (auto I : redecls()) {
     if (I->IsDeleted || I->IsDefaulted || I->Body || I->IsLateTemplateParsed ||
-        I->hasDefiningAttr()) {
+        I->WillHaveBody || I->hasDefiningAttr()) {
       Definition = I->IsDeleted ? I->getCanonicalDecl() : I;
       return true;
     }
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -1873,6 +1873,7 @@
   bool canSkipFunctionBody(Decl *D);
 
   void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope);
+  void ActOnStartFunctionBody(Decl *Decl);
   Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body);
   Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation);
   Decl *ActOnSkippedFunctionBody(Decl *Decl);
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -1839,7 +1839,7 @@
   /// that this returns false for a defaulted function unless that function
   /// has been implicitly defined (possibly as deleted).
   bool isThisDeclarationADefinition() const {
-    return IsDeleted || Body || IsLateTemplateParsed;
+    return IsDeleted || Body || IsLateTemplateParsed || WillHaveBody;
   }
 
   /// doesThisDeclarationHaveABody - Returns whether this specific
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D30375: Function with... Serge Pavlov via Phabricator via cfe-commits

Reply via email to