Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -992,6 +992,82 @@ return false; } +static bool FriendMembersDifferByConstraints(Sema &S, DeclContext *CurContext, + FunctionDecl *Old, + FunctionDecl *New) { + // Only necessary/valid if we're instantiating a + // ClassTemplateSpecializationDecl. + if (CurContext->getDeclKind() != Decl::ClassTemplateSpecialization) + return false; + + // Only when we're dealing with a friend function. + if (!Old->getFriendObjectKind() || !New->getFriendObjectKind()) + return false; + + // If the the two functions share lexical declaration context, they are not in + // separate instantations. + if (New->getLexicalDeclContext() == Old->getLexicalDeclContext()) + return false; + + auto *OldLexCtx = + dyn_cast(Old->getLexicalDeclContext()); + auto *NewLexCtx = + dyn_cast(New->getLexicalDeclContext()); + + if (!OldLexCtx || !NewLexCtx) + return false; + + auto InitAssocConstrs = [](FunctionDecl *FD, + SmallVectorImpl &AC) { + if (const auto *FT = FD->getDescribedFunctionTemplate()) + FT->getAssociatedConstraints(AC); + else + FD->getAssociatedConstraints(AC); + }; + SmallVector OldAC; + SmallVector NewAC; + InitAssocConstrs(Old, OldAC); + InitAssocConstrs(New, NewAC); + + assert(OldAC.size() == NewAC.size() && + "Should not have been identical unless constraints were the same?"); + + // At this point, we know the constraints lists should be the same. + auto *OldBegin = OldAC.begin(); + auto *NewBegin = NewAC.begin(); + + auto SubstConstraint = [](Sema &S, ClassTemplateSpecializationDecl *LexCtx, + const Expr *Constraint) { + Sema::ContextRAII SavedContext(S, LexCtx); + LocalInstantiationScope Scope(S); + EnterExpressionEvaluationContext EvalCtx( + S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + Sema::SFINAETrap Trap(S); + return S.SubstConstraintExpr(const_cast(Constraint), + S.getTemplateInstantiationArgs(LexCtx)); + }; + + for (; OldBegin != OldAC.end(); ++OldBegin, ++NewBegin) { + ExprResult OldConverted = SubstConstraint(S, OldLexCtx, *OldBegin); + ExprResult NewConverted = SubstConstraint(S, NewLexCtx, *NewBegin); + + // We can consider these different, since they depend on the instantiation, + // and cannot prove they are identical. + if (OldConverted.isInvalid() || NewConverted.isInvalid()) + return true; + + // profile & compare. If they are now different, they aren't equal. + llvm::FoldingSetNodeID NewID, OldID; + NewConverted.get()->Profile(NewID, S.getASTContext(), /*Canonical=*/true); + OldConverted.get()->Profile(OldID, S.getASTContext(), /*Canonical=*/true); + if (NewID != OldID) + return true; + } + + // If we haven't found a differing constraint, these are the same. + return false; +} + /// Determine whether the given New declaration is an overload of the /// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if /// New and Old cannot be overloaded, e.g., if New has the same signature as @@ -1068,6 +1144,20 @@ !shouldLinkPossiblyHiddenDecl(*I, New)) continue; + // If this is a friend function currently being instantiated as a part + // of a ClassTemplateSpecializationDecl, it could have + // otherwise-identical-looking constraints that depend on the current + // instantiation. In this case, if the otherwise apparent 'match' and + // the new declaration differ by lexical declaration context (meaning + // different Class Template Specializations), AND one of the collected + // constraints seems to 'depend' on the current instantiation in some + // way, than these are not matches and are likely instead overloads. + // Note that an 'error' case might still be valid later (as it could be + // something that would be 'changed' at 'checking' time), but is proof + // we depend on the Class Template Specialization. + if (FriendMembersDifferByConstraints(*this, CurContext, OldF, New)) + continue; + Match = *I; return Ovl_Match; } Index: clang/test/SemaTemplate/concepts.cpp =================================================================== --- clang/test/SemaTemplate/concepts.cpp +++ clang/test/SemaTemplate/concepts.cpp @@ -502,3 +502,201 @@ // expected-note@#CMV_FOO2_REQ{{because 'IsIntt)>' evaluated to false}} } } // namespace DeferredInstantiationInstScope + +namespace LibCXXOperatorRedef { +template struct is_same { + static constexpr bool value = false; +}; +template struct is_same { + static constexpr bool value = false; +}; + +template +concept same_as = is_same::value; + +// An issue found from libcxx when trying to commit the deferred concepts patch. +// This caused an error of 'redefinition of funcN'. +template struct __range_adaptor_closure { + template + requires same_as<_Tp, _Closure> + friend constexpr decltype(auto) R1func1(_View &&__view, + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R1func2(_View &&__view, + _Closure &&__closure) + requires same_as<_Tp, _Closure> + {}; + template _View, typename _Closure> + friend constexpr decltype(auto) R1func3(_View &&__view, + _Closure &&__closure) + {}; +}; + +struct A : __range_adaptor_closure {}; +struct B : __range_adaptor_closure {}; + +// These three fail because after the 1st pass of instantiation, they are still +// identical. +template struct __range_adaptor_closure2 { + template + requires same_as<_View, _Closure> + friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1 + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2 + _Closure &&__closure) + requires same_as<_View, _Closure> + {}; + template _Closure> + friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3 + _Closure &&__closure){}; +}; + +struct A2 : __range_adaptor_closure2 {}; +struct B2 : __range_adaptor_closure2 {}; +// expected-error@#FUNC1{{redefinition of 'R2func1'}} +// expected-note@-2{{in instantiation of template class}} +// expected-note@#FUNC1{{previous definition is here}} +// expected-error@#FUNC2{{redefinition of 'R2func2'}} +// expected-note@#FUNC2{{previous definition is here}} +// expected-error@#FUNC3{{redefinition of 'R2func3'}} +// expected-note@#FUNC3{{previous definition is here}} + +// These three are fine, they all depend on the parent template parameter, so +// are different despite ::type not being valid. +template struct __range_adaptor_closure3 { + template + requires same_as + friend constexpr decltype(auto) R3func1(_View &&__view, + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R3func2(_View &&__view, + _Closure &&__closure) + requires same_as + {}; + template _View, typename _Closure> + friend constexpr decltype(auto) R3func3(_View &&__view, + _Closure &&__closure) + {}; +}; + +struct A3 : __range_adaptor_closure3 {}; +struct B3 : __range_adaptor_closure3 {}; + +template struct __range_adaptor_closure4 { + template + requires same_as<_Tp, _View> + // expected-note@+1{{previous definition is here}} + void foo1(_View &&, _Closure &&) {} + template + requires same_as<_Tp, _View> + // expected-error@+1{{class member cannot be redeclared}} + void foo1(_View &&, _Closure &&) {} + + template + // expected-note@+1{{previous definition is here}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + template + // expected-error@+1{{class member cannot be redeclared}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + + template _View, typename _Closure> + // expected-note@+1{{previous definition is here}} + void foo3(_View &&, _Closure &&) + {} + template _View, typename _Closure> + // expected-error@+1{{class member cannot be redeclared}} + void foo3(_View &&, _Closure &&) + {} +}; + +// Requires instantiation to fail, so no errors here. +template struct __range_adaptor_closure5 { + template U> + friend void foo(){} + template U> + friend void foo(){} +}; + +template struct __range_adaptor_closure6 { + template U> + friend void foo(){} // #RAC6FOO1 + template U> + friend void foo(){} // #RAC6FOO2 +}; +struct A6 : __range_adaptor_closure6 {}; +//expected-error@#RAC6FOO2{{redefinition of 'foo'}} +//expected-note@-2{{in instantiation of template class}} +//expected-note@#RAC6FOO1{{previous definition is here}} + +template struct S1 { + template + friend void dupe(){} // #S1DUPE + + template + requires same_as + friend void dupe2(){} // #S1DUPE2 +}; +template struct S2 { + template + friend void dupe(){} // #S2DUPE + + template + requires same_as + friend void dupe2(){} // #S2DUPE2 +}; + +template struct S3 { + template + requires same_as + friend void dupe(){} // #S3DUPE +}; +template struct S4 { + template + requires same_as + friend void dupe(){} // #S4DUPE +}; + +// Same as S3 and S4, but aren't instantiated with the same T. +template struct S5 { + template + requires same_as + friend void not_dupe(){} +}; +template struct S6 { + template + requires same_as + friend void not_dupe(){} +}; + +template struct S7 { + void not_dupe() requires same_as{} +}; + +void useS() { + S1 s1; + S2 s2; + // expected-error@#S2DUPE{{redefinition}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#S1DUPE{{previous definition is here}} + // expected-error@#S2DUPE2{{redefinition}} + // expected-note@#S1DUPE2{{previous definition is here}} + + S3 s3; + S4 s4; + // expected-error@#S4DUPE{{redefinition}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#S3DUPE{{previous definition is here}} + + // OK, because only instantiated with different T. + S5 s5; + S6 s6; + + S7 s7; +} + +} // namespace LibCXXOperatorRedef