Commit ea49dcd1 authored by Tom Stellard's avatar Tom Stellard
Browse files

Merging r341775:

------------------------------------------------------------------------
r341775 | rsmith | 2018-09-09 22:32:13 -0700 (Sun, 09 Sep 2018) | 2 lines

Part of PR33222: defer enforcing return type mismatch for dependent
friend function declarations of class templates.
------------------------------------------------------------------------

llvm-svn: 345409
parent 9e981fb6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1950,6 +1950,8 @@ public:
                                FunctionDecl *NewFD, LookupResult &Previous,
                                bool IsMemberSpecialization);
  bool shouldLinkDependentDeclWithPrevious(Decl *D, Decl *OldDecl);
  bool canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD,
                                      QualType NewT, QualType OldT);
  void CheckMain(FunctionDecl *FD, const DeclSpec &D);
  void CheckMSVCRTEntryPoint(FunctionDecl *FD);
  Attr *getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD, bool IsDefinition);
+60 −19
Original line number Diff line number Diff line
@@ -3258,8 +3258,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
             ? New->getTypeSourceInfo()->getType()->castAs<FunctionType>()
             : NewType)->getReturnType();
    if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType) &&
        !((NewQType->isDependentType() || OldQType->isDependentType()) &&
          New->isLocalExternDecl())) {
        canFullyTypeCheckRedeclaration(New, Old, NewDeclaredReturnType,
                                       OldDeclaredReturnType)) {
      QualType ResQT;
      if (NewDeclaredReturnType->isObjCObjectPointerType() &&
          OldDeclaredReturnType->isObjCObjectPointerType())
@@ -3427,13 +3427,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
    if (OldQTypeForComparison == NewQType)
      return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld);
    if ((NewQType->isDependentType() || OldQType->isDependentType()) &&
        New->isLocalExternDecl()) {
      // It's OK if we couldn't merge types for a local function declaraton
      // if either the old or new type is dependent. We'll merge the types
      // when we instantiate the function.
    // If the types are imprecise (due to dependent constructs in friends or
    // local extern declarations), it's OK if they differ. We'll check again
    // during instantiation.
    if (!canFullyTypeCheckRedeclaration(New, Old, NewQType, OldQType))
      return false;
    }
    // Fall through for conflicting redeclarations and redefinitions.
  }
@@ -9336,6 +9334,39 @@ Attr *Sema::getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD,
  }
  return nullptr;
}
/// Determines if we can perform a correct type check for \p D as a
/// redeclaration of \p PrevDecl. If not, we can generally still perform a
/// best-effort check.
///
/// \param NewD The new declaration.
/// \param OldD The old declaration.
/// \param NewT The portion of the type of the new declaration to check.
/// \param OldT The portion of the type of the old declaration to check.
bool Sema::canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD,
                                          QualType NewT, QualType OldT) {
  if (!NewD->getLexicalDeclContext()->isDependentContext())
    return true;
  // For dependently-typed local extern declarations and friends, we can't
  // perform a correct type check in general until instantiation:
  //
  //   int f();
  //   template<typename T> void g() { T f(); }
  //
  // (valid if g() is only instantiated with T = int).
  if (NewT->isDependentType() &&
      (NewD->isLocalExternDecl() || NewD->getFriendObjectKind()))
    return false;
  // Similarly, if the previous declaration was a dependent local extern
  // declaration, we don't really know its type yet.
  if (OldT->isDependentType() && OldD->isLocalExternDecl())
    return false;
  return true;
}
/// Checks if the new declaration declared in dependent context must be
/// put in the same redeclaration chain as the specified declaration.
///
@@ -9346,20 +9377,30 @@ Attr *Sema::getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD,
///          belongs to.
///
bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) {
  // Any declarations should be put into redeclaration chains except for
  // friend declaration in a dependent context that names a function in
  // namespace scope.
  //
  // This allows to compile code like:
  if (!D->getLexicalDeclContext()->isDependentContext())
    return true;
  // Don't chain dependent friend function definitions until instantiation, to
  // permit cases like
  //
  //   void func();
  //   template<typename T> class C1 { friend void func() {} };
  //   template<typename T> class C2 { friend void func() {} };
  //
  // This code snippet is a valid code unless both templates are instantiated.
  return !(D->getLexicalDeclContext()->isDependentContext() &&
           D->getDeclContext()->isFileContext() &&
           D->getFriendObjectKind() != Decl::FOK_None);
  // ... which is valid if only one of C1 and C2 is ever instantiated.
  //
  // FIXME: This need only apply to function definitions. For now, we proxy
  // this by checking for a file-scope function. We do not want this to apply
  // to friend declarations nominating member functions, because that gets in
  // the way of access checks.
  if (D->getFriendObjectKind() && D->getDeclContext()->isFileContext())
    return false;
  auto *VD = dyn_cast<ValueDecl>(D);
  auto *PrevVD = dyn_cast<ValueDecl>(PrevDecl);
  return !VD || !PrevVD ||
         canFullyTypeCheckRedeclaration(VD, PrevVD, VD->getType(),
                                        PrevVD->getType());
}
namespace MultiVersioning {
+23 −0
Original line number Diff line number Diff line
@@ -388,3 +388,26 @@ namespace default_arg {
    friend void f(void *p = 0) {} // expected-error {{must be the only}}
  };
}

namespace PR33222 {
  int f();
  template<typename T> struct X {
    friend T f();
  };
  X<int> xi;

  int g(); // expected-note {{previous}}
  template<typename T> struct Y {
    friend T g(); // expected-error {{return type}}
  };
  Y<float> yf; // expected-note {{instantiation}}

  int h();
  template<typename T> struct Z {
    // FIXME: The note here should point at the non-friend declaration, not the
    // instantiation in Z<int>.
    friend T h(); // expected-error {{return type}} expected-note {{previous}}
  };
  Z<int> zi;
  Z<float> zf; // expected-note {{instantiation}}
}