Skip to content

Stop evaluating trailing requires clause after overload resolution #6997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -5346,11 +5346,22 @@ class Sema final {
// Expression Parsing Callbacks: SemaExpr.cpp.

bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid);
// A version of DiagnoseUseOfDecl that should be used if overload resolution
// has been used to find this declaration, which means we don't have to bother
// checking the trailing requires clause.
bool DiagnoseUseOfOverloadedDecl(NamedDecl *D, SourceLocation Loc) {
return DiagnoseUseOfDecl(
D, Loc, /*UnknownObjCClass=*/nullptr, /*ObjCPropertyAccess=*/false,
/*AvoidPartialAvailabilityChecks=*/false, /*ClassReceiver=*/nullptr,
/*SkipTrailingRequiresClause=*/true);
}

bool DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
const ObjCInterfaceDecl *UnknownObjCClass = nullptr,
bool ObjCPropertyAccess = false,
bool AvoidPartialAvailabilityChecks = false,
ObjCInterfaceDecl *ClassReciever = nullptr);
ObjCInterfaceDecl *ClassReciever = nullptr,
bool SkipTrailingRequiresClause = false);
void NoteDeletedFunction(FunctionDecl *FD);
void NoteDeletedInheritingConstructor(CXXConstructorDecl *CD);
bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD,
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15592,7 +15592,10 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
SourceRange ParenRange) {
if (auto *Shadow = dyn_cast<ConstructorUsingShadowDecl>(FoundDecl)) {
Constructor = findInheritingConstructor(ConstructLoc, Constructor, Shadow);
if (DiagnoseUseOfDecl(Constructor, ConstructLoc))
// The only way to get here is if we did overlaod resolution to find the
// shadow decl, so we don't need to worry about re-checking the trailing
// requires clause.
if (DiagnoseUseOfOverloadedDecl(Constructor, ConstructLoc))
return ExprError();
}

Expand Down
9 changes: 5 additions & 4 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
const ObjCInterfaceDecl *UnknownObjCClass,
bool ObjCPropertyAccess,
bool AvoidPartialAvailabilityChecks,
ObjCInterfaceDecl *ClassReceiver) {
ObjCInterfaceDecl *ClassReceiver,
bool SkipTrailingRequiresClause) {
SourceLocation Loc = Locs.front();
if (getLangOpts().CPlusPlus && isa<FunctionDecl>(D)) {
// If there were any diagnostics suppressed by template argument deduction,
Expand Down Expand Up @@ -281,7 +282,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
// See if this is a function with constraints that need to be satisfied.
// Check this before deducing the return type, as it might instantiate the
// definition.
if (FD->getTrailingRequiresClause()) {
if (!SkipTrailingRequiresClause && FD->getTrailingRequiresClause()) {
ConstraintSatisfaction Satisfaction;
if (CheckFunctionConstraints(FD, Satisfaction, Loc,
/*ForOverloadResolution*/ true))
Expand Down Expand Up @@ -6776,8 +6777,8 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
nullptr, DRE->isNonOdrUse());
}
}
} else if (isa<MemberExpr>(NakedFn))
NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
} else if (auto *ME = dyn_cast<MemberExpr>(NakedFn))
NDecl = ME->getMemberDecl();

if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {
if (CallingNDeclIndirectly && !checkAddressOfFunctionIsAvailable(
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14694,7 +14694,7 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
Method = cast<CXXMethodDecl>(Best->Function);
FoundDecl = Best->FoundDecl;
CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl);
if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc()))
if (DiagnoseUseOfOverloadedDecl(Best->FoundDecl, UnresExpr->getNameLoc()))
break;
// If FoundDecl is different from Method (such as if one is a template
// and the other a specialization), make sure DiagnoseUseOfDecl is
Expand All @@ -14703,7 +14703,7 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
// DiagnoseUseOfDecl to accept both the FoundDecl and the decl
// being used.
if (Method != FoundDecl.getDecl() &&
DiagnoseUseOfDecl(Method, UnresExpr->getNameLoc()))
DiagnoseUseOfOverloadedDecl(Method, UnresExpr->getNameLoc()))
break;
Succeeded = true;
break;
Expand Down
57 changes: 57 additions & 0 deletions clang/test/SemaTemplate/concepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,3 +708,60 @@ Container<5>::var_templ<int> inst_fail;
// expected-note@#CMVT_REQ{{because 'sizeof(int) == arity' (4 == 5) evaluated to false}}
} // namespace ConstrainedMemberVarTemplate

// These should not diagnose, where we were unintentionally doing so before by
// checking trailing requires clause twice, yet not having the ability to the
// 2nd time, since it was no longer a dependent variant.
namespace InheritedFromPartialSpec {
template<class C>
constexpr bool Check = true;

template<typename T>
struct Foo {
template<typename U>
Foo(U&&) requires (Check<U>){}
template<typename U>
void MemFunc(U&&) requires (Check<U>){}
template<typename U>
static void StaticMemFunc(U&&) requires (Check<U>){}
~Foo() requires (Check<T>){}
};

template<>
struct Foo<void> : Foo<int> {
using Foo<int>::Foo;
using Foo<int>::MemFunc;
using Foo<int>::StaticMemFunc;
};

void use() {
Foo<void> F {1.1};
F.MemFunc(1.1);
Foo<void>::StaticMemFunc(1.1);
}

template<typename T>
struct counted_iterator {
constexpr auto operator->() const noexcept requires false {
return T::Invalid;
};
};

template<class _Ip>
concept __has_member_pointer = requires { typename _Ip::pointer; };

template<class>
struct __iterator_traits_member_pointer_or_arrow_or_void { using type = void; };
template<__has_member_pointer _Ip>
struct __iterator_traits_member_pointer_or_arrow_or_void<_Ip> { using type = typename _Ip::pointer; };

template<class _Ip>
requires requires(_Ip& __i) { __i.operator->(); } && (!__has_member_pointer<_Ip>)
struct __iterator_traits_member_pointer_or_arrow_or_void<_Ip> {
using type = decltype(declval<_Ip&>().operator->());
};


void use2() {
__iterator_traits_member_pointer_or_arrow_or_void<counted_iterator<int>> f;
}
}// namespace InheritedFromPartialSpec