From 1be59c510612d911352ffc2ca06ab344e21eea4e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 22 Oct 2016 01:32:19 +0000 Subject: [PATCH] [c++1z] P0012R1: Implement a few remaining pieces: downgrade diagnostic for mismatched dynamic exception specifications in expressions from an error to a warning, since this is no longer ill-formed in C++1z. Allow reference binding of a reference-to-non-noexcept function to a noexcept function lvalue. As defect resolutions, also allow a conditional between noexcept and non-noexcept function lvalues to produce a non-noexcept function lvalue (rather than decaying to a function pointer), and allow function template argument deduction to deduce a reference to non-noexcept function when binding to a noexcept function type. llvm-svn: 284905 --- clang/include/clang/Basic/DiagnosticGroups.td | 2 + clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++ clang/include/clang/Sema/Sema.h | 20 +++--- clang/lib/Sema/SemaExceptionSpec.cpp | 72 +++++++++++++++------- clang/lib/Sema/SemaExprCXX.cpp | 28 +++++---- clang/lib/Sema/SemaOverload.cpp | 10 +++ clang/lib/Sema/SemaTemplateDeduction.cpp | 12 +++- clang/test/CXX/drs/dr0xx.cpp | 12 +++- clang/test/CXX/drs/dr13xx.cpp | 13 +++- .../CXX/expr/expr.post/expr.static.cast/p7.cpp | 10 +++ clang/test/CXX/expr/p13.cpp | 8 +-- clang/test/CXX/over/over.over/p1.cpp | 21 ++++++- .../temp.deduct/temp.deduct.call/p4.cpp | 16 ++++- .../temp.deduct/temp.deduct.conv/p5.cpp | 22 ++++++- clang/www/cxx_dr_status.html | 2 +- clang/www/make_cxx_dr_status | 8 ++- 16 files changed, 199 insertions(+), 61 deletions(-) create mode 100644 clang/test/CXX/expr/expr.post/expr.static.cast/p7.cpp diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 7796522..7b58818 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -621,6 +621,8 @@ def Format2 : DiagGroup<"format=2", def TypeSafety : DiagGroup<"type-safety">; +def IncompatibleExceptionSpec : DiagGroup<"incompatible-exception-spec">; + def IntToVoidPointerCast : DiagGroup<"int-to-void-pointer-cast">; def IntToPointerCast : DiagGroup<"int-to-pointer-cast", [IntToVoidPointerCast]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 974f3bd..9a2f59f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1294,8 +1294,12 @@ def ext_override_exception_spec : ExtWarn, InGroup; def err_incompatible_exception_specs : Error< "target exception specification is not superset of source">; +def warn_incompatible_exception_specs : Warning< + err_incompatible_exception_specs.Text>, InGroup; def err_deep_exception_specs_differ : Error< "exception specifications of %select{return|argument}0 types differ">; +def warn_deep_exception_specs_differ : Warning< + err_deep_exception_specs_differ.Text>, InGroup; def err_missing_exception_specification : Error< "%0 is missing exception specification '%1'">; def ext_missing_exception_specification : ExtWarn< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index be5a0bc..a26a47c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1334,13 +1334,19 @@ public: bool *MissingEmptyExceptionSpecification = nullptr, bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false); - bool CheckExceptionSpecSubset( - const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, - const FunctionProtoType *Superset, SourceLocation SuperLoc, - const FunctionProtoType *Subset, SourceLocation SubLoc); - bool CheckParamExceptionSpec(const PartialDiagnostic & NoteID, - const FunctionProtoType *Target, SourceLocation TargetLoc, - const FunctionProtoType *Source, SourceLocation SourceLoc); + bool CheckExceptionSpecSubset(const PartialDiagnostic &DiagID, + const PartialDiagnostic &NestedDiagID, + const PartialDiagnostic &NoteID, + const FunctionProtoType *Superset, + SourceLocation SuperLoc, + const FunctionProtoType *Subset, + SourceLocation SubLoc); + bool CheckParamExceptionSpec(const PartialDiagnostic &NestedDiagID, + const PartialDiagnostic &NoteID, + const FunctionProtoType *Target, + SourceLocation TargetLoc, + const FunctionProtoType *Source, + SourceLocation SourceLoc); TypeResult ActOnTypeName(Scope *S, Declarator &D); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 74c5b42..ae72842 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -414,7 +414,7 @@ bool Sema::CheckEquivalentExceptionSpec( /// a problem. If \c true is returned, either a diagnostic has already been /// produced or \c *MissingExceptionSpecification is set to \c true. bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, - const PartialDiagnostic & NoteID, + const PartialDiagnostic &NoteID, const FunctionProtoType *Old, SourceLocation OldLoc, const FunctionProtoType *New, @@ -605,10 +605,13 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, /// CheckExceptionSpecSubset - Check whether the second function type's /// exception specification is a subset (or equivalent) of the first function /// type. This is used by override and pointer assignment checks. -bool Sema::CheckExceptionSpecSubset( - const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, - const FunctionProtoType *Superset, SourceLocation SuperLoc, - const FunctionProtoType *Subset, SourceLocation SubLoc) { +bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID, + const PartialDiagnostic &NestedDiagID, + const PartialDiagnostic &NoteID, + const FunctionProtoType *Superset, + SourceLocation SuperLoc, + const FunctionProtoType *Subset, + SourceLocation SubLoc) { // Just auto-succeed under -fno-exceptions. if (!getLangOpts().CXXExceptions) @@ -632,7 +635,8 @@ bool Sema::CheckExceptionSpecSubset( // If superset contains everything, we're done. if (SuperEST == EST_None || SuperEST == EST_MSAny) - return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, + Subset, SubLoc); // If there are dependent noexcept specs, assume everything is fine. Unlike // with the equivalency check, this is safe in this case, because we don't @@ -647,7 +651,8 @@ bool Sema::CheckExceptionSpecSubset( // Another case of the superset containing everything. if (SuperNR == FunctionProtoType::NR_Throw) - return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, + Subset, SubLoc); ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); @@ -678,7 +683,8 @@ bool Sema::CheckExceptionSpecSubset( // If the subset contains nothing, we're done. if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow) - return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, + Subset, SubLoc); // Otherwise, if the superset contains nothing, we've failed. if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) { @@ -770,14 +776,15 @@ bool Sema::CheckExceptionSpecSubset( } } // We've run half the gauntlet. - return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); + return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, + Subset, SubLoc); } -static bool CheckSpecForTypesEquivalent(Sema &S, - const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, - QualType Target, SourceLocation TargetLoc, - QualType Source, SourceLocation SourceLoc) -{ +static bool +CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID, + const PartialDiagnostic &NoteID, QualType Target, + SourceLocation TargetLoc, QualType Source, + SourceLocation SourceLoc) { const FunctionProtoType *TFunc = GetUnderlyingFunction(Target); if (!TFunc) return false; @@ -794,13 +801,16 @@ static bool CheckSpecForTypesEquivalent(Sema &S, /// assignment and override compatibility check. We do not check the parameters /// of parameter function pointers recursively, as no sane programmer would /// even be able to write such a function type. -bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &NoteID, +bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &DiagID, + const PartialDiagnostic &NoteID, const FunctionProtoType *Target, SourceLocation TargetLoc, const FunctionProtoType *Source, SourceLocation SourceLoc) { + auto RetDiag = DiagID; + RetDiag << 0; if (CheckSpecForTypesEquivalent( - *this, PDiag(diag::err_deep_exception_specs_differ) << 0, PDiag(), + *this, RetDiag, PDiag(), Target->getReturnType(), TargetLoc, Source->getReturnType(), SourceLoc)) return true; @@ -810,8 +820,10 @@ bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &NoteID, assert(Target->getNumParams() == Source->getNumParams() && "Functions have different argument counts."); for (unsigned i = 0, E = Target->getNumParams(); i != E; ++i) { + auto ParamDiag = DiagID; + ParamDiag << 1; if (CheckSpecForTypesEquivalent( - *this, PDiag(diag::err_deep_exception_specs_differ) << 1, PDiag(), + *this, ParamDiag, PDiag(), Target->getParamType(i), TargetLoc, Source->getParamType(i), SourceLoc)) return true; @@ -831,6 +843,16 @@ bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) { if (!FromFunc || FromFunc->hasDependentExceptionSpec()) return false; + unsigned DiagID = diag::err_incompatible_exception_specs; + unsigned NestedDiagID = diag::err_deep_exception_specs_differ; + // This is not an error in C++17 onwards, unless the noexceptness doesn't + // match, but in that case we have a full-on type mismatch, not just a + // type sugar mismatch. + if (getLangOpts().CPlusPlus1z) { + DiagID = diag::warn_incompatible_exception_specs; + NestedDiagID = diag::warn_deep_exception_specs_differ; + } + // Now we've got the correct types on both sides, check their compatibility. // This means that the source of the conversion can only throw a subset of // the exceptions of the target, and any exception specs on arguments or @@ -843,10 +865,10 @@ bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) { // void (*q)(void (*) throw(int)) = p; // } // ... because it might be instantiated with T=int. - return CheckExceptionSpecSubset(PDiag(diag::err_incompatible_exception_specs), - PDiag(), ToFunc, - From->getSourceRange().getBegin(), - FromFunc, SourceLocation()); + return CheckExceptionSpecSubset(PDiag(DiagID), PDiag(NestedDiagID), PDiag(), + ToFunc, From->getSourceRange().getBegin(), + FromFunc, SourceLocation()) && + !getLangOpts().CPlusPlus1z; } bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, @@ -880,6 +902,7 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, if (getLangOpts().MicrosoftExt) DiagID = diag::ext_override_exception_spec; return CheckExceptionSpecSubset(PDiag(DiagID), + PDiag(diag::err_deep_exception_specs_differ), PDiag(diag::note_overridden_virtual_function), Old->getType()->getAs(), Old->getLocation(), @@ -902,8 +925,13 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) { // See if we can get a function type from the decl somehow. const ValueDecl *VD = dyn_cast(D); - if (!VD) // If we have no clue what we're calling, assume the worst. + if (!VD) { + // In C++17, we may have a canonical exception specification. If so, use it. + if (auto *FT = E->getType().getCanonicalType()->getAs()) + return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can; + // If we have no clue what we're calling, assume the worst. return CT_Can; + } // As an extension, we assume that __attribute__((nothrow)) functions don't // throw. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 6ef7df1..0dab27a 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5370,23 +5370,29 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, // if both are glvalues of the same value category and the same type except // for cv-qualification, an attempt is made to convert each of those // operands to the type of the other. + // FIXME: + // Resolving a defect in P0012R1: we extend this to cover all cases where + // one of the operands is reference-compatible with the other, in order + // to support conditionals between functions differing in noexcept. ExprValueKind LVK = LHS.get()->getValueKind(); ExprValueKind RVK = RHS.get()->getValueKind(); if (!Context.hasSameType(LTy, RTy) && - Context.hasSameUnqualifiedType(LTy, RTy) && LVK == RVK && LVK != VK_RValue) { - // Since the unqualified types are reference-related and we require the - // result to be as if a reference bound directly, the only conversion - // we can perform is to add cv-qualifiers. - Qualifiers LCVR = Qualifiers::fromCVRMask(LTy.getCVRQualifiers()); - Qualifiers RCVR = Qualifiers::fromCVRMask(RTy.getCVRQualifiers()); - if (RCVR.isStrictSupersetOf(LCVR)) { - LHS = ImpCastExprToType(LHS.get(), RTy, CK_NoOp, LVK); - LTy = LHS.get()->getType(); - } - else if (LCVR.isStrictSupersetOf(RCVR)) { + // DerivedToBase was already handled by the class-specific case above. + // FIXME: Should we allow ObjC conversions here? + bool DerivedToBase, ObjCConversion, ObjCLifetimeConversion; + if (CompareReferenceRelationship( + QuestionLoc, LTy, RTy, DerivedToBase, + ObjCConversion, ObjCLifetimeConversion) == Ref_Compatible && + !DerivedToBase && !ObjCConversion && !ObjCLifetimeConversion) { RHS = ImpCastExprToType(RHS.get(), LTy, CK_NoOp, RVK); RTy = RHS.get()->getType(); + } else if (CompareReferenceRelationship( + QuestionLoc, RTy, LTy, DerivedToBase, + ObjCConversion, ObjCLifetimeConversion) == Ref_Compatible && + !DerivedToBase && !ObjCConversion && !ObjCLifetimeConversion) { + LHS = ImpCastExprToType(LHS.get(), RTy, CK_NoOp, LVK); + LTy = LHS.get()->getType(); } } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index a39ad58..42bc14b 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -4172,6 +4172,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, DerivedToBase = false; ObjCConversion = false; ObjCLifetimeConversion = false; + QualType ConvertedT2; if (UnqualT1 == UnqualT2) { // Nothing to do. } else if (isCompleteType(Loc, OrigT2) && @@ -4182,6 +4183,15 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, UnqualT2->isObjCObjectOrInterfaceType() && Context.canBindObjCObjectType(UnqualT1, UnqualT2)) ObjCConversion = true; + else if (UnqualT2->isFunctionType() && + IsFunctionConversion(UnqualT2, UnqualT1, ConvertedT2)) + // C++1z [dcl.init.ref]p4: + // cv1 T1" is reference-compatible with "cv2 T2" if [...] T2 is "noexcept + // function" and T1 is "function" + // + // We extend this to also apply to 'noreturn', so allow any function + // conversion between function types. + return Ref_Compatible; else return Ref_Incompatible; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 2e4b9ca..e61197b 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2729,6 +2729,13 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, // We don't want to keep the reference around any more. OriginalParamType = OriginalParamRef->getPointeeType(); + // FIXME: Resolve core issue (no number yet): if the original P is a + // reference type and the transformed A is function type "noexcept F", + // the deduced A can be F. + QualType Tmp; + if (A->isFunctionType() && S.IsFunctionConversion(A, DeducedA, Tmp)) + return false; + Qualifiers AQuals = A.getQualifiers(); Qualifiers DeducedAQuals = DeducedA.getQualifiers(); @@ -2760,8 +2767,8 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, // type that can be converted to the deduced A via a function pointer // conversion and/or a qualification conversion. // - // Also allow conversions which merely strip [[noreturn]] from function types - // (recursively) as an extension. + // Also allow conversions which merely strip __attribute__((noreturn)) from + // function types (recursively). bool ObjCLifetimeConversion = false; QualType ResultTy; if ((A->isAnyPointerType() || A->isMemberPointerType()) && @@ -2770,7 +2777,6 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, S.IsFunctionConversion(A, DeducedA, ResultTy))) return false; - // - If P is a class and P has the form simple-template-id, then the // transformed A can be a derived class of the deduced A. [...] // [...] Likewise, if P is a pointer to a class of the form diff --git a/clang/test/CXX/drs/dr0xx.cpp b/clang/test/CXX/drs/dr0xx.cpp index 2e895ed..25ddf99 100644 --- a/clang/test/CXX/drs/dr0xx.cpp +++ b/clang/test/CXX/drs/dr0xx.cpp @@ -961,10 +961,11 @@ namespace dr85 { // dr85: yes // dr86: dup 446 namespace dr87 { // dr87: no + // FIXME: Superseded by dr1975 template struct X {}; // FIXME: This is invalid. X x; - // ... but this is valid. + // This is valid under dr87 but not under dr1975. X y; } @@ -1013,9 +1014,14 @@ namespace dr91 { // dr91: yes int k = f(U()); } -namespace dr92 { // FIXME: Issue is still open. +namespace dr92 { // dr92: 4.0 c++17 void f() throw(int, float); - void (*p)() throw(int) = &f; // expected-error {{target exception specification is not superset of source}} + void (*p)() throw(int) = &f; +#if __cplusplus <= 201402L + // expected-error@-2 {{target exception specification is not superset of source}} +#else + // expected-warning@-4 {{target exception specification is not superset of source}} +#endif void (*q)() throw(int); void (**pp)() throw() = &q; #if __cplusplus <= 201402L diff --git a/clang/test/CXX/drs/dr13xx.cpp b/clang/test/CXX/drs/dr13xx.cpp index b7d3a34..103ceee 100644 --- a/clang/test/CXX/drs/dr13xx.cpp +++ b/clang/test/CXX/drs/dr13xx.cpp @@ -54,8 +54,19 @@ namespace dr1330 { // dr1330: 4.0 c++11 // the "T has not yet been instantiated" error here, rather than giving // confusing errors later on. #endif - void (B

::*bpf2)() throw(int) = &B

::f; // expected-error-re {{{{not superset|different exception spec}}}} + void (B

::*bpf2)() throw(int) = &B

::f; +#if __cplusplus <= 201402L + // expected-error@-2 {{not superset}} +#else + // expected-warning@-4 {{not superset}} +#endif void (B

::*bpf3)() = &B

::f; + void (B

::*bpf4)() throw() = &B

::f; +#if __cplusplus <= 201402L + // expected-error@-2 {{not superset}} +#else + // expected-error@-4 {{different exception specifications}} +#endif #if __cplusplus >= 201103L static_assert(noexcept(B

().g()), ""); diff --git a/clang/test/CXX/expr/expr.post/expr.static.cast/p7.cpp b/clang/test/CXX/expr/expr.post/expr.static.cast/p7.cpp new file mode 100644 index 0000000..fd8e478 --- /dev/null +++ b/clang/test/CXX/expr/expr.post/expr.static.cast/p7.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions + +void (*p)() noexcept; +void (*q)(); + +void f() { + // FIXME: This seems like a bad rule. + p = static_cast(q); // expected-error {{not allowed}} + q = static_cast(p); +} diff --git a/clang/test/CXX/expr/p13.cpp b/clang/test/CXX/expr/p13.cpp index b5f1587..00ed923 100644 --- a/clang/test/CXX/expr/p13.cpp +++ b/clang/test/CXX/expr/p13.cpp @@ -40,12 +40,12 @@ namespace dynamic_exception_spec { using Y = void (*)() throw(float); using Z = void (*)() throw(int, float); void g(X x, Y y, Z z, bool k) { - x = k ? X() : Y(); // expected-error {{not superset}} - y = k ? X() : Y(); // expected-error {{not superset}} + x = k ? X() : Y(); // expected-warning {{not superset}} + y = k ? X() : Y(); // expected-warning {{not superset}} z = k ? X() : Y(); - x = k ? x : y; // expected-error {{not superset}} - y = k ? x : y; // expected-error {{not superset}} + x = k ? x : y; // expected-warning {{not superset}} + y = k ? x : y; // expected-warning {{not superset}} z = k ? x : y; } } diff --git a/clang/test/CXX/over/over.over/p1.cpp b/clang/test/CXX/over/over.over/p1.cpp index 430d1a2..e31a2c5 100644 --- a/clang/test/CXX/over/over.over/p1.cpp +++ b/clang/test/CXX/over/over.over/p1.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -DNOEXCEPT= %s -// RUN: %clang_cc1 -fsyntax-only -std=c++1z -DNOEXCEPT= %s -// FIXME: %clang_cc1 -fsyntax-only -std=c++1z -DNOEXCEPT=noexcept %s +// RUN: %clang_cc1 -fsyntax-only -DNOEXCEPT= -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++1z -DNOEXCEPT= -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++1z -DNOEXCEPT=noexcept -verify %s template T f0(T) NOEXCEPT; int f0(int) NOEXCEPT; @@ -94,3 +94,18 @@ Y1 y1; Y1<&f0> y1a; Y2 y2; Y3 y3; + +#if __cplusplus > 201402L +namespace MixedNoexcept { + inline namespace A { + void f() noexcept; // expected-note {{candidate}} + } + inline namespace B { + void f(); // expected-note {{candidate}} + } + void (*p)() noexcept = &f; // ok + void (*q)() = &f; // expected-error {{ambiguous}} +} +#else +// expected-no-diagnostics +#endif diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp index cf9003b..e7b665a 100644 --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp @@ -30,16 +30,26 @@ namespace noexcept_conversion { bar(&f); } // There is no corresponding rule for references. - // FIXME: This seems like a defect. - template void baz(R(&)()); // expected-note {{does not match adjusted type}} + // We consider this to be a defect, and allow deduction to succeed in this + // case. FIXME: Check this should be accepted once the DR is resolved. + template void baz(R(&)()); void g() { - baz(f); // expected-error {{no match}} + baz(f); } + // But there is one for member pointers. + template void quux(R (C::*)(A...)); + struct Q { void f(int, char) noexcept { quux(&Q::f); } }; + void g1() noexcept; void g2(); template int h(T *, T *); // expected-note {{deduced conflicting types for parameter 'T' ('void () noexcept' vs. 'void ()')}} int x = h(g1, g2); // expected-error {{no matching function}} + + // FIXME: It seems like a defect that B is not deducible here. + template int i(void () noexcept(B)); // expected-note 2{{couldn't infer template argument 'B'}} + int i1 = i(g1); // expected-error {{no matching function}} + int i2 = i(g2); // expected-error {{no matching function}} } #else // expected-no-diagnostics diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p5.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p5.cpp index ed7b000..8821d53 100644 --- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p5.cpp +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p5.cpp @@ -31,13 +31,31 @@ void (A::*q4)() noexcept = D(); // There is no corresponding rule for references. // FIXME: This seems like a defect. +// FIXME: We don't actually implement the final check for equal types at all! +// Instead, we handle the matching via [over.ics.user]p3: +// "If the user-defined conversion is specified by a specialization of a +// conversion function template, the second standard conversion sequence +// shall have exact match rank." +// Note that this *does* allow discarding noexcept, since that conversion has +// Exact Match rank. struct E { template operator Fn&(); // expected-note {{candidate}} }; struct F { - template operator Fn&(); // expected-note {{candidate}} + template operator Fn&(); }; void (&r1)() = E(); -void (&r2)() = F(); // expected-error {{no viable conversion}} +void (&r2)() = F(); void (&r3)() noexcept = E(); // expected-error {{no viable conversion}} void (&r4)() noexcept = F(); + +// FIXME: We reject this for entirely the wrong reason. We incorrectly succeed +// in deducing T = void, U = G::B, and only fail due to [over.ics.user]p3. +struct G { + template struct A {}; + template struct A : A {}; + struct B { typedef int type; }; + + template operator A *(); // expected-note {{candidate function [with T = void, U = G::B]}} +}; +G::A *g = G(); // expected-error {{no viable conversion}} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 1385ec3..ad73e7e 100644 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -591,7 +591,7 @@ 92 accepted Should exception-specifications be part of the type system? - Unknown + SVN (C++17 onwards) 93 diff --git a/clang/www/make_cxx_dr_status b/clang/www/make_cxx_dr_status index 9a679ed..3f37217 100755 --- a/clang/www/make_cxx_dr_status +++ b/clang/www/make_cxx_dr_status @@ -99,6 +99,12 @@ def availability(issue): if status.endswith(' c++11'): status = status[:-6] avail_suffix = ' (C++11 onwards)' + elif status.endswith(' c++14'): + status = status[:-6] + avail_suffix = ' (C++14 onwards)' + elif status.endswith(' c++17'): + status = status[:-6] + avail_suffix = ' (C++17 onwards)' if status == 'unknown': avail = 'Unknown' avail_style = ' class="none"' @@ -161,7 +167,7 @@ for dr in drs: %s ''' % (row_style, dr.issue, dr.url, dr.issue, dr.status, dr.title, avail_style, avail) -for status, num in count.items(): +for status, num in sorted(count.items()): print "%s: %s" % (status, num) print >> out_file, '''\ -- 2.7.4