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]>;
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<
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);
/// 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,
/// 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)
// 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
// 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();
// 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) {
}
}
// 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;
/// 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;
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;
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
// 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,
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(),
// 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.
// 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();
}
}
DerivedToBase = false;
ObjCConversion = false;
ObjCLifetimeConversion = false;
+ QualType ConvertedT2;
if (UnqualT1 == UnqualT2) {
// Nothing to do.
} else if (isCompleteType(Loc, OrigT2) &&
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;
// 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();
// 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()) &&
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
// 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;
}
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
// 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()), "");
--- /dev/null
+// 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);
+}
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;
}
}
-// 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;
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
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
// 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}}
<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>
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"'
<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, '''\