-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[Clang][Sema] Explicit template arguments are not substituted into the exception specification of a function #90760
Conversation
@llvm/pr-subscribers-clang Author: Krystian Stasiowski (sdkrystian) Changes[[temp.deduct.general] p6](eel.is/c++draft/temp.deduct.general#6) states: [[temp.deduct.general] p7](eel.is/c++draft/temp.deduct.general#7) goes on to say: Currently, As part of the necessary changes to make this patch work, the instantiation of the exception specification of a function template specialization when taking the address of a function template is changed to only occur for the function selected by overload resolution per [[except.spec] p13.1](http://eel.is/c++draft/except.spec#13.1) (as opposed to being instantiated for every candidate). Full diff: https://github.com/llvm/llvm-project/pull/90760.diff 6 Files Affected:
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 7d9eaf6720461d..2c1c7affbc13a4 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6576,12 +6576,10 @@ void InitializationSequence::InitializeFrom(Sema &S,
AddPassByIndirectCopyRestoreStep(DestType, ShouldCopy);
} else if (ICS.isBad()) {
- DeclAccessPair dap;
- if (isLibstdcxxPointerReturnFalseHack(S, Entity, Initializer)) {
+ if (isLibstdcxxPointerReturnFalseHack(S, Entity, Initializer))
AddZeroInitializationStep(Entity.getType());
- } else if (Initializer->getType() == Context.OverloadTy &&
- !S.ResolveAddressOfOverloadedFunction(Initializer, DestType,
- false, dap))
+ else if (DeclAccessPair Found; Initializer->getType() == Context.OverloadTy &&
+ !S.ResolveAddressOfOverloadedFunction(Initializer, DestType, /*Complain=*/false, Found))
SetFailed(InitializationSequence::FK_AddressOfOverloadFailed);
else if (Initializer->getType()->isFunctionType() &&
isExprAnUnaddressableFunction(S, Initializer))
@@ -9641,6 +9639,8 @@ bool InitializationSequence::Diagnose(Sema &S,
if (!Failed())
return false;
+ QualType DestType = Entity.getType();
+
// When we want to diagnose only one element of a braced-init-list,
// we need to factor it out.
Expr *OnlyArg;
@@ -9650,11 +9650,19 @@ bool InitializationSequence::Diagnose(Sema &S,
OnlyArg = List->getInit(0);
else
OnlyArg = Args[0];
+
+ if (OnlyArg->getType() == S.Context.OverloadTy) {
+ DeclAccessPair Found;
+ if (FunctionDecl *FD = S.ResolveAddressOfOverloadedFunction(
+ OnlyArg, DestType.getNonReferenceType(), /*Complain=*/false, Found)) {
+ if (Expr *Resolved = S.FixOverloadedFunctionReference(OnlyArg, Found, FD).get())
+ OnlyArg = Resolved;
+ }
+ }
}
else
OnlyArg = nullptr;
- QualType DestType = Entity.getType();
switch (Failure) {
case FK_TooManyInitsForReference:
// FIXME: Customize for the initialized entity?
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e93f7bd842e444..fc8a645e733926 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1231,11 +1231,11 @@ bool Sema::isSameOrCompatibleFunctionType(QualType P, QualType A) {
// Noreturn and noexcept adjustment.
QualType AdjustedParam;
if (IsFunctionConversion(P, A, AdjustedParam))
- return Context.hasSameType(AdjustedParam, A);
+ return Context.hasSameFunctionTypeIgnoringExceptionSpec(AdjustedParam, A);
// FIXME: Compatible calling conventions.
- return Context.hasSameType(P, A);
+ return Context.hasSameFunctionTypeIgnoringExceptionSpec(P, A);
}
/// Get the index of the first template parameter that was originally from the
@@ -3414,23 +3414,6 @@ TemplateDeductionResult Sema::SubstituteExplicitTemplateArguments(
if (FunctionType) {
auto EPI = Proto->getExtProtoInfo();
EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size());
-
- // In C++1z onwards, exception specifications are part of the function type,
- // so substitution into the type must also substitute into the exception
- // specification.
- SmallVector<QualType, 4> ExceptionStorage;
- if (getLangOpts().CPlusPlus17 &&
- SubstExceptionSpec(
- Function->getLocation(), EPI.ExceptionSpec, ExceptionStorage,
- getTemplateInstantiationArgs(
- FunctionTemplate, nullptr, /*Final=*/true,
- /*Innermost=*/SugaredExplicitArgumentList->asArray(),
- /*RelativeToPrimary=*/false,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/false,
- /*SkipForSpecialization=*/true)))
- return TemplateDeductionResult::SubstitutionFailure;
-
*FunctionType = BuildFunctionType(ResultType, ParamTypes,
Function->getLocation(),
Function->getDeclName(),
@@ -4610,13 +4593,6 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
Info.getLocation()))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
- auto *SpecializationFPT =
- Specialization->getType()->castAs<FunctionProtoType>();
- if (IsAddressOfFunction && getLangOpts().CPlusPlus17 &&
- isUnresolvedExceptionSpec(SpecializationFPT->getExceptionSpecType()) &&
- !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
- return TemplateDeductionResult::MiscellaneousDeductionFailure;
-
// Adjust the exception specification of the argument to match the
// substituted and resolved type we just formed. (Calling convention and
// noreturn can't be dependent, so we don't actually need this for them
diff --git a/clang/test/CXX/drs/dr13xx.cpp b/clang/test/CXX/drs/dr13xx.cpp
index dad82c4e2829f0..a334b6d01acf51 100644
--- a/clang/test/CXX/drs/dr13xx.cpp
+++ b/clang/test/CXX/drs/dr13xx.cpp
@@ -281,13 +281,10 @@ namespace cwg1330 { // cwg1330: 4 c++11
decltype(f<char>()) f2; // #cwg1330-f-char
bool f3 = noexcept(f<float>()); /// #cwg1330-f-float
#endif
- // In C++17 onwards, substituting explicit template arguments into the
- // function type substitutes into the exception specification (because it's
- // part of the type). In earlier languages, we don't notice there's a problem
- // until we've already started to instantiate.
template int f<short>(); // #cwg1330-f-short
- // since-cxx17-error@-1 {{explicit instantiation of 'f' does not refer to a function template, variable template, member function, member class, or static data member}}
- // since-cxx17-note@#cwg1330-f {{candidate template ignored: substitution failure [with T = short]: type 'short' cannot be used prior to '::' because it has no members}}
+ // since-cxx17-error@#cwg1330-f {{type 'short' cannot be used prior to '::' because it has no members}}
+ // since-cxx17-note@#cwg1330-f {{in instantiation of exception specification for 'f<short>' requested here}}
+ // since-cxx17-note@#cwg1330-f-short {{in instantiation of function template specialization 'cwg1330::f<short>' requested here}}
template<typename T> struct C {
C() throw(typename T::type); // #cwg1330-C
@@ -500,7 +497,7 @@ namespace cwg1359 { // cwg1359: 3.5
union B { constexpr B() = default; int a; }; // #cwg1359-B
// cxx11-17-error@-1 {{defaulted definition of default constructor cannot be marked constexpr before C++23}}
union C { constexpr C() = default; int a, b; }; // #cwg1359-C
- // cxx11-17-error@-1 {{defaulted definition of default constructor cannot be marked constexpr}}
+ // cxx11-17-error@-1 {{defaulted definition of default constructor cannot be marked constexpr}}
struct X { constexpr X() = default; union {}; };
// since-cxx11-error@-1 {{declaration does not declare anything}}
struct Y { constexpr Y() = default; union { int a; }; }; // #cwg1359-Y
@@ -720,7 +717,7 @@ struct A {
} // namespace cwg1397
namespace cwg1399 { // cwg1399: dup 1388
- template<typename ...T> void f(T..., int, T...) {} // #cwg1399-f
+ template<typename ...T> void f(T..., int, T...) {} // #cwg1399-f
// cxx98-error@-1 {{variadic templates are a C++11 extension}}
void g() {
f(0);
diff --git a/clang/test/CXX/temp/temp.deduct/p7.cpp b/clang/test/CXX/temp/temp.deduct/p7.cpp
new file mode 100644
index 00000000000000..cf6d17fc51ac95
--- /dev/null
+++ b/clang/test/CXX/temp/temp.deduct/p7.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -verify %s
+
+struct A {
+ static constexpr bool x = true;
+};
+
+template<typename T, typename U>
+void f(T, U) noexcept(T::x);
+
+template<typename T, typename U>
+void f(T, U*) noexcept(T::y); // expected-error {{no member named 'y' in 'A'}}
+
+template<>
+void f<A>(A, int*); // expected-note {{in instantiation of exception specification}}
diff --git a/clang/test/SemaCXX/cxx1z-noexcept-function-type.cpp b/clang/test/SemaCXX/cxx1z-noexcept-function-type.cpp
index 5e56f19477d6ca..c8204c21523a37 100644
--- a/clang/test/SemaCXX/cxx1z-noexcept-function-type.cpp
+++ b/clang/test/SemaCXX/cxx1z-noexcept-function-type.cpp
@@ -18,7 +18,7 @@ template<typename A, typename B> void redecl3() throw(B); // expected-error {{do
typedef int I;
template<bool B> void redecl4(I) noexcept(B);
-template<bool B> void redecl4(I) noexcept(B); // expected-note {{could not match 'void (I) noexcept(false)' (aka 'void (int) noexcept(false)') against 'void (int) noexcept'}}
+template<bool B> void redecl4(I) noexcept(B);
void (*init_with_exact_type_a)(int) noexcept = redecl4<true>;
void (*init_with_mismatched_type_a)(int) = redecl4<true>;
@@ -27,7 +27,7 @@ using DeducedType_a = decltype(deduce_auto_from_noexcept_function_ptr_a);
using DeducedType_a = void (*)(int) noexcept;
void (*init_with_exact_type_b)(int) = redecl4<false>;
-void (*init_with_mismatched_type_b)(int) noexcept = redecl4<false>; // expected-error {{does not match required type}}
+void (*init_with_mismatched_type_b)(int) noexcept = redecl4<false>; // expected-error {{cannot initialize a variable of type}}
auto deduce_auto_from_noexcept_function_ptr_b = redecl4<false>;
using DeducedType_b = decltype(deduce_auto_from_noexcept_function_ptr_b);
using DeducedType_b = void (*)(int);
diff --git a/clang/test/SemaTemplate/temp_arg_type.cpp b/clang/test/SemaTemplate/temp_arg_type.cpp
index 9069f63e0224fe..cdbcf281125efd 100644
--- a/clang/test/SemaTemplate/temp_arg_type.cpp
+++ b/clang/test/SemaTemplate/temp_arg_type.cpp
@@ -11,7 +11,7 @@ A<0> *a1; // expected-error{{template argument for template type parameter must
A<A> *a2; // expected-error{{use of class template 'A' requires template arguments}}
A<int> *a3;
-A<int()> *a4;
+A<int()> *a4;
A<int(float)> *a5;
A<A<int> > *a6;
@@ -95,15 +95,13 @@ namespace deduce_noexcept {
template void dep() noexcept(true); // expected-error {{does not refer to a function template}}
template void dep() noexcept(false); // expected-error {{does not refer to a function template}}
- // FIXME: It's also not clear whether this should be valid: do we substitute
- // into the function type (including the exception specification) or not?
- template<typename T> typename T::type1 f() noexcept(T::a);
- template<typename T> typename T::type2 f() noexcept(T::b) {}
+ template<typename T> typename T::type1 f() noexcept(T::a); // expected-note {{candidate}}
+ template<typename T> typename T::type2 f() noexcept(T::b) {} // expected-note {{candidate}}
struct X {
static constexpr bool b = true;
using type1 = void;
using type2 = void;
};
- template void f<X>();
+ template void f<X>(); // expected-error {{partial ordering for explicit instantiation of 'f' is ambiguous}}
}
#endif
|
2bc18f4
to
f914b16
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to DR tests look good to me.
✅ With the latest revision this PR passed the C/C++ code formatter. |
Ping @erichkeane |
f914b16
to
6b03580
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I want @Endilll to confirm the DR test looks right though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DR tests changes LGTM
6b03580
to
196871c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…e exception specification of a function
196871c
to
2ac8f1b
Compare
2ac8f1b
to
16ec4ae
Compare
[temp.deduct.general] p6 states:
[temp.deduct.general] p7 goes on to say:
Consider the following:
Currently,
Sema::SubstituteExplicitTemplateArguments
will substitute into the noexcept-specifier when deducing template arguments from a function declaration or when deducing template arguments for taking the address of a function template (and the substitution is treated as a SFINAE context). In the above example,#1
is selected as the primary template because substitution of the explicit template arguments into the noexcept-specifier of#2
failed, which resulted in the candidate being ignored.This behavior is incorrect (note 4 says as much), and this patch corrects it by deferring all substitution into the noexcept-specifier until it is instantiated.
As part of the necessary changes to make this patch work, the instantiation of the exception specification of a function template specialization when taking the address of a function template is changed to only occur for the function selected by overload resolution per [except.spec] p13.1 (as opposed to being instantiated for every candidate).