[clang] [Clang][Sema] Explicit template arguments are not substituted into the exception specification of a function (PR #90760)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Tue May 7 11:45:16 PDT 2024
https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/90760
>From e5e0f25de3307128914b6fcfc9223b389e7b6438 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 1 May 2024 10:54:12 -0400
Subject: [PATCH 1/5] [Clang][Sema] Explicit template arguments are not
substituted into the exception specification of a function
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 17 -----------------
clang/test/CXX/temp/temp.deduct/p7.cpp | 14 ++++++++++++++
2 files changed, 14 insertions(+), 17 deletions(-)
create mode 100644 clang/test/CXX/temp/temp.deduct/p7.cpp
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 9f9e4422827173..026ce6db1c8eb9 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3509,23 +3509,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(),
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}}
>From 3e31517090dd899c9161406ad38626ecaca8c787 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 1 May 2024 14:14:28 -0400
Subject: [PATCH 2/5] [FOLD]
---
clang/lib/Sema/SemaInit.cpp | 24 ++++++++++++++-----
clang/lib/Sema/SemaTemplateDeduction.cpp | 11 ++-------
clang/test/CXX/drs/dr13xx.cpp | 13 ++++------
.../SemaCXX/cxx1z-noexcept-function-type.cpp | 4 ++--
clang/test/SemaTemplate/temp_arg_type.cpp | 10 ++++----
5 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 7d9eaf6720461d..c8049ae581f843 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6576,12 +6576,12 @@ 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 +9641,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 +9652,21 @@ 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 026ce6db1c8eb9..942b84488004fd 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1325,11 +1325,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
@@ -4688,13 +4688,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 at -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 at -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 at -1 {{defaulted definition of default constructor cannot be marked constexpr}}
+ // cxx11-17-error at -1 {{defaulted definition of default constructor cannot be marked constexpr}}
struct X { constexpr X() = default; union {}; };
// since-cxx11-error at -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 at -1 {{variadic templates are a C++11 extension}}
void g() {
f(0);
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
>From 99722a2abd003a7be4abf7708d21c705e00eb465 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Mon, 6 May 2024 06:40:53 -0400
Subject: [PATCH 3/5] [FOLD] add release note
---
clang/docs/ReleaseNotes.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a85095e424b64b..bce947153b0aab 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -683,6 +683,8 @@ Bug Fixes to C++ Support
- Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447)
- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243)
and (#GH88832).
+- Clang now defers all substitution into the exception specification of a function template specialization
+ until the noexcept-specifier is instantiated.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
>From a1450f24f06c28959dbe664448f1d588fe2b5944 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Mon, 6 May 2024 12:50:52 -0400
Subject: [PATCH 4/5] [FOLD] refactor isSameOrCompatibleFunctionType
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 942b84488004fd..dcaea4a77bffd2 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1323,12 +1323,10 @@ bool Sema::isSameOrCompatibleFunctionType(QualType P, QualType A) {
return Context.hasSameType(P, A);
// Noreturn and noexcept adjustment.
- QualType AdjustedParam;
- if (IsFunctionConversion(P, A, AdjustedParam))
- return Context.hasSameFunctionTypeIgnoringExceptionSpec(AdjustedParam, A);
+ if (QualType AdjustedParam; IsFunctionConversion(P, A, AdjustedParam))
+ P = AdjustedParam;
// FIXME: Compatible calling conventions.
-
return Context.hasSameFunctionTypeIgnoringExceptionSpec(P, A);
}
>From 2ac8f1bee74640d571918dec3fd82373b9ceafbb Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 7 May 2024 14:43:17 -0400
Subject: [PATCH 5/5] [FOLD] add additional test
---
clang/test/CXX/except/except.spec/p13.cpp | 28 +++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/clang/test/CXX/except/except.spec/p13.cpp b/clang/test/CXX/except/except.spec/p13.cpp
index 61cdb74f21ec51..813f1ebfeb5040 100644
--- a/clang/test/CXX/except/except.spec/p13.cpp
+++ b/clang/test/CXX/except/except.spec/p13.cpp
@@ -72,3 +72,31 @@ template<>
void f(A, int***); // expected-error {{'f<A, int>' is missing exception specification 'noexcept'}}
}
+
+namespace N3 {
+
+template<typename T, typename U>
+void f(T, U) noexcept(T::y); // #1
+
+template<typename T, typename U> // #2
+void f(T, U*) noexcept(T::x);
+
+// Deduction should succeed for both candidates, and #2 should be selected by overload resolution.
+// Only the exception specification of #2 should be instantiated.
+void (*x)(A, int*) noexcept = f;
+}
+
+namespace N4 {
+
+template<typename T, typename U>
+void f(T, U) noexcept(T::x); // #1
+
+template<typename T, typename U>
+void f(T, U*) noexcept(T::y); // #2
+// expected-error at -1 {{no member named 'y' in 'A'}}
+
+// Deduction should succeed for both candidates, and #2 should be selected by overload resolution.
+// Only the exception specification of #2 should be instantiated.
+void (*x)(A, int*) noexcept = f; // expected-error {{cannot initialize a variable of type 'void (*)(A, int *) noexcept' with an lvalue of type 'void (A, int *)': different exception specifications}}
+ // expected-note at -1 {{in instantiation of exception specification for 'f<A, int>' requested here}}
+}
More information about the cfe-commits
mailing list