forked from OSchip/llvm-project
[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
This commit is contained in:
parent
738837eed9
commit
1be59c5106
clang
include/clang
lib/Sema
test/CXX
drs
expr
over/over.over
temp/temp.fct.spec/temp.deduct
www
|
@ -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]>;
|
||||
|
|
|
@ -1294,8 +1294,12 @@ def ext_override_exception_spec : ExtWarn<err_override_exception_spec.Text>,
|
|||
InGroup<MicrosoftExceptionSpec>;
|
||||
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<IncompatibleExceptionSpec>;
|
||||
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<IncompatibleExceptionSpec>;
|
||||
def err_missing_exception_specification : Error<
|
||||
"%0 is missing exception specification '%1'">;
|
||||
def ext_missing_exception_specification : ExtWarn<
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<FunctionProtoType>(),
|
||||
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<ValueDecl>(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<FunctionProtoType>())
|
||||
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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -961,10 +961,11 @@ namespace dr85 { // dr85: yes
|
|||
// dr86: dup 446
|
||||
|
||||
namespace dr87 { // dr87: no
|
||||
// FIXME: Superseded by dr1975
|
||||
template<typename T> struct X {};
|
||||
// FIXME: This is invalid.
|
||||
X<void() throw()> x;
|
||||
// ... but this is valid.
|
||||
// This is valid under dr87 but not under dr1975.
|
||||
X<void(void() throw())> 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
|
||||
|
|
|
@ -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<P>::*bpf2)() throw(int) = &B<P>::f; // expected-error-re {{{{not superset|different exception spec}}}}
|
||||
void (B<P>::*bpf2)() throw(int) = &B<P>::f;
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{not superset}}
|
||||
#else
|
||||
// expected-warning@-4 {{not superset}}
|
||||
#endif
|
||||
void (B<P>::*bpf3)() = &B<P>::f;
|
||||
void (B<P>::*bpf4)() throw() = &B<P>::f;
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{not superset}}
|
||||
#else
|
||||
// expected-error@-4 {{different exception specifications}}
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
static_assert(noexcept(B<P>().g()), "");
|
||||
|
|
|
@ -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<decltype(p)>(q); // expected-error {{not allowed}}
|
||||
q = static_cast<decltype(q)>(p);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<typename T> T f0(T) NOEXCEPT;
|
||||
int f0(int) NOEXCEPT;
|
||||
|
@ -94,3 +94,18 @@ Y1<f0> y1;
|
|||
Y1<&f0> y1a;
|
||||
Y2<f0> y2;
|
||||
Y3<f0> 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
|
||||
|
|
|
@ -30,16 +30,26 @@ namespace noexcept_conversion {
|
|||
bar(&f);
|
||||
}
|
||||
// There is no corresponding rule for references.
|
||||
// FIXME: This seems like a defect.
|
||||
template<typename R> 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<typename R> void baz(R(&)());
|
||||
void g() {
|
||||
baz(f); // expected-error {{no match}}
|
||||
baz(f);
|
||||
}
|
||||
|
||||
// But there is one for member pointers.
|
||||
template<typename R, typename C, typename ...A> void quux(R (C::*)(A...));
|
||||
struct Q { void f(int, char) noexcept { quux(&Q::f); } };
|
||||
|
||||
void g1() noexcept;
|
||||
void g2();
|
||||
template <class T> 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<bool B> 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
|
||||
|
|
|
@ -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<typename T> operator Fn<T, false>&(); // expected-note {{candidate}}
|
||||
};
|
||||
struct F {
|
||||
template<typename T> operator Fn<T, true>&(); // expected-note {{candidate}}
|
||||
template<typename T> operator Fn<T, true>&();
|
||||
};
|
||||
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<typename, typename> struct A {};
|
||||
template<typename U> struct A<U, int> : A<U, void> {};
|
||||
struct B { typedef int type; };
|
||||
|
||||
template<typename T, typename U = B> operator A<T, typename U::type> *(); // expected-note {{candidate function [with T = void, U = G::B]}}
|
||||
};
|
||||
G::A<void, void> *g = G(); // expected-error {{no viable conversion}}
|
||||
|
|
|
@ -591,7 +591,7 @@
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#92">92</a></td>
|
||||
<td>accepted</td>
|
||||
<td>Should <I>exception-specification</I>s be part of the type system?</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="svn" align="center">SVN (C++17 onwards)</td>
|
||||
</tr>
|
||||
<tr id="93">
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#93">93</a></td>
|
||||
|
|
|
@ -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:
|
|||
<td%s align="center">%s</td>
|
||||
</tr>''' % (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, '''\
|
||||
|
|
Loading…
Reference in New Issue