From e5e0f25de3307128914b6fcfc9223b389e7b6438 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski 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 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 +void f(T, U) noexcept(T::x); + +template +void f(T, U*) noexcept(T::y); // expected-error {{no member named 'y' in 'A'}} + +template<> +void f(A, int*); // expected-note {{in instantiation of exception specification}} From 3e31517090dd899c9161406ad38626ecaca8c787 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski 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(); - 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()) f2; // #cwg1330-f-char bool f3 = noexcept(f()); /// #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(); // #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' requested here}} + // since-cxx17-note@#cwg1330-f-short {{in instantiation of function template specialization 'cwg1330::f' requested here}} template 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 void f(T..., int, T...) {} // #cwg1399-f + template 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/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 void redecl3() throw(B); // expected-error {{do typedef int I; template void redecl4(I) noexcept(B); -template void redecl4(I) noexcept(B); // expected-note {{could not match 'void (I) noexcept(false)' (aka 'void (int) noexcept(false)') against 'void (int) noexcept'}} +template void redecl4(I) noexcept(B); void (*init_with_exact_type_a)(int) noexcept = redecl4; void (*init_with_mismatched_type_a)(int) = redecl4; @@ -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; -void (*init_with_mismatched_type_b)(int) noexcept = redecl4; // expected-error {{does not match required type}} +void (*init_with_mismatched_type_b)(int) noexcept = redecl4; // expected-error {{cannot initialize a variable of type}} auto deduce_auto_from_noexcept_function_ptr_b = redecl4; 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 *a2; // expected-error{{use of class template 'A' requires template arguments}} A *a3; -A *a4; +A *a4; A *a5; A > *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::type1 f() noexcept(T::a); - template typename T::type2 f() noexcept(T::b) {} + template typename T::type1 f() noexcept(T::a); // expected-note {{candidate}} + template 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(); + template void f(); // expected-error {{partial ordering for explicit instantiation of 'f' is ambiguous}} } #endif From 99722a2abd003a7be4abf7708d21c705e00eb465 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski 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 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 16ec4aec6b2ee65608e9e3961fa00e31bc4a9b0a Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski 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 | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/clang/test/CXX/except/except.spec/p13.cpp b/clang/test/CXX/except/except.spec/p13.cpp index 61cdb74f21ec51..29390c277c5203 100644 --- a/clang/test/CXX/except/except.spec/p13.cpp +++ b/clang/test/CXX/except/except.spec/p13.cpp @@ -72,3 +72,30 @@ template<> void f(A, int***); // expected-error {{'f' is missing exception specification 'noexcept'}} } + +namespace N3 { + +template +void f(T, U) noexcept(T::y); // #1 + +template // #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*) = f; +} + +namespace N4 { + +template +void f(T, U) noexcept(T::x); // #1 + +template +void f(T, U*) noexcept(T::y); // #2 +// expected-error@-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*) = f; // expected-note {{in instantiation of exception specification for 'f' requested here}} +}