return reinterpret_cast<FunctionDecl *const *>(param_type_end())[1];
}
/// Determine whether this function type has a non-throwing exception
+ /// specification.
+ CanThrowResult canThrow(const ASTContext &Ctx) const;
+ /// Determine whether this function type has a non-throwing exception
/// specification. If this depends on template arguments, returns
/// \c ResultIfDependent.
- bool isNothrow(const ASTContext &Ctx, bool ResultIfDependent = false) const;
+ bool isNothrow(const ASTContext &Ctx, bool ResultIfDependent = false) const {
+ return ResultIfDependent ? canThrow(Ctx) != CT_Can
+ : canThrow(Ctx) == CT_Cannot;
+ }
bool isVariadic() const { return Variadic; }
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}5 vs "
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
- "volatile and restrict|const, volatile, and restrict}6)}4">;
+ "volatile and restrict|const, volatile, and restrict}6)"
+ "|: different exception specifications}4">;
def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot "
"bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">;
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
"|volatile and restrict|const, volatile, and restrict}3 but found "
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
- "|volatile and restrict|const, volatile, and restrict}4)}2">;
+ "|volatile and restrict|const, volatile, and restrict}4)"
+ "| has different exception specification}2">;
def note_ovl_candidate_inherited_constructor : Note<
"constructor from base class %0 inherited here">;
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}2 vs "
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
- "volatile and restrict|const, volatile, and restrict}3)}1">;
+ "volatile and restrict|const, volatile, and restrict}3)"
+ "|: different exception specifications}1">;
def warn_using_directive_in_header : Warning<
"using namespace directive in global context in header">,
InGroup<HeaderHygiene>, DefaultIgnore;
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}5 vs "
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
- "volatile and restrict|const, volatile, and restrict}6)}4">;
+ "volatile and restrict|const, volatile, and restrict}6)"
+ "|: different exception specifications}4">;
def err_typecheck_missing_return_type_incompatible : Error<
"%diff{return type $ must match previous return type $|"
"return type must match previous return type}0,1 when %select{block "
ICK_Lvalue_To_Rvalue, ///< Lvalue-to-rvalue conversion (C++ 4.1)
ICK_Array_To_Pointer, ///< Array-to-pointer conversion (C++ 4.2)
ICK_Function_To_Pointer, ///< Function-to-pointer (C++ 4.3)
- ICK_NoReturn_Adjustment, ///< Removal of noreturn from a type (Clang)
+ ICK_Function_Conversion, ///< Function pointer conversion (C++17 4.13)
ICK_Qualification, ///< Qualification conversions (C++ 4.4)
ICK_Integral_Promotion, ///< Integral promotions (C++ 4.5)
ICK_Floating_Promotion, ///< Floating point promotions (C++ 4.6)
bool IgnoreBaseAccess);
bool IsQualificationConversion(QualType FromType, QualType ToType,
bool CStyle, bool &ObjCLifetimeConversion);
- bool IsNoReturnConversion(QualType FromType, QualType ToType,
+ bool IsFunctionConversion(QualType FromType, QualType ToType,
QualType &ResultTy);
bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
bool isSameOrCompatibleFunctionType(CanQualType Param, CanQualType Arg);
return CanResultType;
}
+static bool isCanonicalExceptionSpecification(
+ const FunctionProtoType::ExceptionSpecInfo &ESI, bool NoexceptInType) {
+ if (ESI.Type == EST_None)
+ return true;
+ if (!NoexceptInType)
+ return false;
+
+ // C++17 onwards: exception specification is part of the type, as a simple
+ // boolean "can this function type throw".
+ if (ESI.Type == EST_BasicNoexcept)
+ return true;
+
+ // A dynamic exception specification is canonical if it only contains pack
+ // expansions (so we can't tell whether it's non-throwing).
+ if (ESI.Type == EST_Dynamic) {
+ for (QualType ET : ESI.Exceptions)
+ if (!ET->getAs<PackExpansionType>())
+ return false;
+ return true;
+ }
+
+ // A noexcept(expr) specification is canonical if expr is value-dependent.
+ if (ESI.Type == EST_ComputedNoexcept)
+ return ESI.NoexceptExpr && ESI.NoexceptExpr->isValueDependent();
+
+ return false;
+}
+
QualType
ASTContext::getFunctionType(QualType ResultTy, ArrayRef<QualType> ArgArray,
const FunctionProtoType::ExtProtoInfo &EPI) const {
FunctionProtoTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(FTP, 0);
+ bool NoexceptInType = getLangOpts().CPlusPlus1z;
+
+ bool IsCanonicalExceptionSpec =
+ isCanonicalExceptionSpecification(EPI.ExceptionSpec, NoexceptInType);
+
// Determine whether the type being created is already canonical or not.
- bool isCanonical =
- EPI.ExceptionSpec.Type == EST_None && isCanonicalResultType(ResultTy) &&
- !EPI.HasTrailingReturn;
+ bool isCanonical = IsCanonicalExceptionSpec &&
+ isCanonicalResultType(ResultTy) && !EPI.HasTrailingReturn;
for (unsigned i = 0; i != NumArgs && isCanonical; ++i)
if (!ArgArray[i].isCanonicalAsParam())
isCanonical = false;
FunctionProtoType::ExtProtoInfo CanonicalEPI = EPI;
CanonicalEPI.HasTrailingReturn = false;
- CanonicalEPI.ExceptionSpec = FunctionProtoType::ExceptionSpecInfo();
+
+ if (IsCanonicalExceptionSpec) {
+ // Exception spec is already OK.
+ } else if (NoexceptInType) {
+ switch (EPI.ExceptionSpec.Type) {
+ case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated:
+ // We don't know yet. It shouldn't matter what we pick here; no-one
+ // should ever look at this.
+ LLVM_FALLTHROUGH;
+ case EST_None: case EST_MSAny: case EST_Dynamic:
+ // If we get here for EST_Dynamic, there is at least one
+ // non-pack-expansion type, so this is not non-throwing.
+ CanonicalEPI.ExceptionSpec.Type = EST_None;
+ break;
+
+ case EST_DynamicNone: case EST_BasicNoexcept:
+ CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
+ break;
+
+ case EST_ComputedNoexcept:
+ llvm::APSInt Value(1);
+ auto *E = CanonicalEPI.ExceptionSpec.NoexceptExpr;
+ if (!E || !E->isIntegerConstantExpr(Value, *this, nullptr,
+ /*IsEvaluated*/false)) {
+ // This noexcept specification is invalid.
+ // FIXME: Should this be able to happen?
+ CanonicalEPI.ExceptionSpec.Type = EST_None;
+ break;
+ }
+
+ CanonicalEPI.ExceptionSpec.Type =
+ Value.getBoolValue() ? EST_BasicNoexcept : EST_None;
+ break;
+ }
+ assert(isCanonicalExceptionSpecification(CanonicalEPI.ExceptionSpec,
+ NoexceptInType));
+ } else {
+ CanonicalEPI.ExceptionSpec = FunctionProtoType::ExceptionSpecInfo();
+ }
// Adjust the canonical function result type.
CanQualType CanResultTy = getCanonicalFunctionResultType(ResultTy);
QualType *exnSlot = argSlot + NumParams;
unsigned I = 0;
for (QualType ExceptionType : epi.ExceptionSpec.Exceptions) {
- // Note that a dependent exception specification does *not* make
- // a type dependent; it's not even part of the C++ type system.
+ // Note that, before C++17, a dependent exception specification does
+ // *not* make a type dependent; it's not even part of the C++ type
+ // system.
if (ExceptionType->isInstantiationDependentType())
setInstantiationDependent();
slot[0] = epi.ExceptionSpec.SourceDecl;
}
+ // If this is a canonical type, and its exception specification is dependent,
+ // then it's a dependent type. This only happens in C++17 onwards.
+ if (isCanonicalUnqualified()) {
+ if (getExceptionSpecType() == EST_Dynamic ||
+ getExceptionSpecType() == EST_ComputedNoexcept) {
+ assert(hasDependentExceptionSpec() && "type should not be canonical");
+ setDependent();
+ }
+ } else if (getCanonicalTypeInternal()->isDependentType()) {
+ // Ask our canonical type whether our exception specification was dependent.
+ setDependent();
+ }
+
if (epi.ExtParameterInfos) {
ExtParameterInfo *extParamInfos =
const_cast<ExtParameterInfo *>(getExtParameterInfosBuffer());
return value.getBoolValue() ? NR_Nothrow : NR_Throw;
}
-bool FunctionProtoType::isNothrow(const ASTContext &Ctx,
- bool ResultIfDependent) const {
+CanThrowResult FunctionProtoType::canThrow(const ASTContext &Ctx) const {
ExceptionSpecificationType EST = getExceptionSpecType();
assert(EST != EST_Unevaluated && EST != EST_Uninstantiated);
if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
- return true;
+ return CT_Cannot;
- if (EST == EST_Dynamic && ResultIfDependent) {
+ if (EST == EST_Dynamic) {
// A dynamic exception specification is throwing unless every exception
// type is an (unexpanded) pack expansion type.
for (unsigned I = 0, N = NumExceptions; I != N; ++I)
if (!getExceptionType(I)->getAs<PackExpansionType>())
- return false;
- return ResultIfDependent;
+ return CT_Can;
+ return CT_Dependent;
}
if (EST != EST_ComputedNoexcept)
- return false;
+ return CT_Can;
NoexceptResult NR = getNoexceptSpec(Ctx);
if (NR == NR_Dependent)
- return ResultIfDependent;
- return NR == NR_Nothrow;
+ return CT_Dependent;
+ return NR == NR_Nothrow ? CT_Cannot : CT_Can;
}
bool FunctionProtoType::isTemplateVariadic() const {
}
if (getLangOpts().CPlusPlus) {
- // (C++98 13.1p2):
+ // C++1z [over.load]p2
// Certain function declarations cannot be overloaded:
- // -- Function declarations that differ only in the return type
- // cannot be overloaded.
+ // -- Function declarations that differ only in the return type,
+ // the exception specification, or both cannot be overloaded.
+
+ // Check the exception specifications match. This may recompute the type of
+ // both Old and New if it resolved exception specifications, so grab the
+ // types again after this. Because this updates the type, we do this before
+ // any of the other checks below, which may update the "de facto" NewQType
+ // but do not necessarily update the type of New.
+ if (CheckEquivalentExceptionSpec(Old, New))
+ return true;
+ OldQType = Context.getCanonicalType(Old->getType());
+ NewQType = Context.getCanonicalType(New->getType());
// Go back to the type source info to compare the declared return types,
// per C++1y [dcl.type.auto]p13:
(New->getTypeSourceInfo()
? New->getTypeSourceInfo()->getType()->castAs<FunctionType>()
: NewType)->getReturnType();
- QualType ResQT;
if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType) &&
!((NewQType->isDependentType() || OldQType->isDependentType()) &&
New->isLocalExternDecl())) {
+ QualType ResQT;
if (NewDeclaredReturnType->isObjCObjectPointerType() &&
OldDeclaredReturnType->isObjCObjectPointerType())
ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType);
// noreturn should now match unless the old type info didn't have it.
QualType OldQTypeForComparison = OldQType;
if (!OldTypeInfo.getNoReturn() && NewTypeInfo.getNoReturn()) {
- assert(OldQType == QualType(OldType, 0));
+ auto *OldType = OldQType->castAs<FunctionProtoType>();
const FunctionType *OldTypeForComparison
= Context.adjustFunctionType(OldType, OldTypeInfo.withNoReturn(true));
OldQTypeForComparison = QualType(OldTypeForComparison, 0);
Invalid = true;
}
- if (CheckEquivalentExceptionSpec(Old, New))
- Invalid = true;
-
return Invalid;
}
/// to member to a function with an exception specification. This means that
/// it is invalid to add another level of indirection.
bool Sema::CheckDistantExceptionSpec(QualType T) {
+ // C++17 removes this rule in favor of putting exception specifications into
+ // the type system.
+ if (getLangOpts().CPlusPlus1z)
+ return false;
+
if (const PointerType *PT = T->getAs<PointerType>())
T = PT->getPointeeType();
else if (const MemberPointerType *PT = T->getAs<MemberPointerType>())
return Sema::IncompatiblePointer;
}
if (!S.getLangOpts().CPlusPlus &&
- S.IsNoReturnConversion(ltrans, rtrans, ltrans))
+ S.IsFunctionConversion(ltrans, rtrans, ltrans))
return Sema::IncompatiblePointer;
return ConvTy;
}
// Nothing else to do.
break;
- case ICK_NoReturn_Adjustment:
+ case ICK_Function_Conversion:
// If both sides are functions (or pointers/references to them), there could
// be incompatible exception declarations.
if (CheckExceptionSpecCompatibility(From, ToType))
"Lvalue-to-rvalue",
"Array-to-pointer",
"Function-to-pointer",
- "Noreturn adjustment",
+ "Function pointer conversion",
"Qualification",
"Integral promotion",
"Floating point promotion",
}
/// \brief Determine whether the conversion from FromType to ToType is a valid
-/// conversion that strips "noreturn" off the nested function type.
-bool Sema::IsNoReturnConversion(QualType FromType, QualType ToType,
+/// conversion that strips "noexcept" or "noreturn" off the nested function
+/// type.
+bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
QualType &ResultTy) {
if (Context.hasSameUnqualifiedType(FromType, ToType))
return false;
// Permit the conversion F(t __attribute__((noreturn))) -> F(t)
+ // or F(t noexcept) -> F(t)
// where F adds one of the following at most once:
// - a pointer
// - a member pointer
return false;
}
- const FunctionType *FromFn = cast<FunctionType>(CanFrom);
- FunctionType::ExtInfo EInfo = FromFn->getExtInfo();
- if (!EInfo.getNoReturn()) return false;
+ const auto *FromFn = cast<FunctionType>(CanFrom);
+ FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo();
+
+ const auto *ToFn = dyn_cast<FunctionProtoType>(CanTo);
+ FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo();
+
+ bool Changed = false;
+
+ // Drop 'noreturn' if not present in target type.
+ if (FromEInfo.getNoReturn() && !ToEInfo.getNoReturn()) {
+ FromFn = Context.adjustFunctionType(FromFn, FromEInfo.withNoReturn(false));
+ Changed = true;
+ }
+
+ // Drop 'noexcept' if not present in target type.
+ if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) {
+ const auto *ToFPT = dyn_cast<FunctionProtoType>(ToFn);
+ if (FromFPT->isNothrow(Context) && !ToFPT->isNothrow(Context)) {
+ FromFn = cast<FunctionType>(
+ Context.getFunctionType(FromFPT->getReturnType(),
+ FromFPT->getParamTypes(),
+ FromFPT->getExtProtoInfo().withExceptionSpec(
+ FunctionProtoType::ExceptionSpecInfo()))
+ .getTypePtr());
+ Changed = true;
+ }
+ }
+
+ if (!Changed)
+ return false;
- FromFn = Context.adjustFunctionType(FromFn, EInfo.withNoReturn(false));
assert(QualType(FromFn, 0).isCanonical());
if (QualType(FromFn, 0) != CanTo) return false;
S.ExtractUnqualifiedFunctionType(ToType), FromType)) {
QualType resultTy;
// if the function type matches except for [[noreturn]], it's ok
- if (!S.IsNoReturnConversion(FromType,
+ if (!S.IsFunctionConversion(FromType,
S.ExtractUnqualifiedFunctionType(ToType), resultTy))
// otherwise, only a boolean conversion is standard
if (!ToType->isBooleanType())
// Compatible conversions (Clang extension for C function overloading)
SCS.Second = ICK_Compatible_Conversion;
FromType = ToType.getUnqualifiedType();
- } else if (S.IsNoReturnConversion(FromType, ToType, FromType)) {
- // Treat a conversion that strips "noreturn" as an identity conversion.
- SCS.Second = ICK_NoReturn_Adjustment;
+ } else if (S.IsFunctionConversion(FromType, ToType, FromType)) {
+ // Function pointer conversions (removing 'noexcept') including removal of
+ // 'noreturn' (Clang extension).
+ SCS.Second = ICK_Function_Conversion;
} else if (IsTransparentUnionStandardConversion(S, From, ToType,
InOverloadResolution,
SCS, CStyle)) {
ft_parameter_arity,
ft_parameter_mismatch,
ft_return_type,
- ft_qualifer_mismatch
+ ft_qualifer_mismatch,
+ ft_noexcept
};
/// Attempts to get the FunctionProtoType from a Type. Handles
return;
}
+ // Handle exception specification differences on canonical type (in C++17
+ // onwards).
+ if (cast<FunctionProtoType>(FromFunction->getCanonicalTypeUnqualified())
+ ->isNothrow(Context) !=
+ cast<FunctionProtoType>(ToFunction->getCanonicalTypeUnqualified())
+ ->isNothrow(Context)) {
+ PDiag << ft_noexcept;
+ return;
+ }
+
// Unable to find a difference, so add no extra info.
PDiag << ft_default;
}
// conversions are fine.
switch (SCS.Second) {
case ICK_Identity:
- case ICK_NoReturn_Adjustment:
+ case ICK_Function_Conversion:
case ICK_Integral_Promotion:
case ICK_Integral_Conversion: // Narrowing conversions are checked elsewhere.
return true;
bool candidateHasExactlyCorrectType(const FunctionDecl *FD) {
QualType Discard;
return Context.hasSameUnqualifiedType(TargetFunctionType, FD->getType()) ||
- S.IsNoReturnConversion(FD->getType(), TargetFunctionType, Discard);
+ S.IsFunctionConversion(FD->getType(), TargetFunctionType, Discard);
}
/// \return true if A is considered a better overload candidate for the
if (!ParamFunction || !ArgFunction)
return Param == Arg;
- // Noreturn adjustment.
+ // Noreturn and noexcept adjustment.
QualType AdjustedParam;
- if (IsNoReturnConversion(Param, Arg, AdjustedParam))
+ if (IsFunctionConversion(Param, Arg, AdjustedParam))
return Arg == Context.getCanonicalType(AdjustedParam);
// FIXME: Compatible calling conventions.
}
// - The transformed A can be another pointer or pointer to member
- // type that can be converted to the deduced A via a qualification
- // conversion.
+ // 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.
- // FIXME: Currently, this doesn't play nicely with qualification conversions.
bool ObjCLifetimeConversion = false;
QualType ResultTy;
if ((A->isAnyPointerType() || A->isMemberPointerType()) &&
(S.IsQualificationConversion(A, DeducedA, false,
ObjCLifetimeConversion) ||
- S.IsNoReturnConversion(A, DeducedA, ResultTy)))
+ S.IsFunctionConversion(A, DeducedA, ResultTy)))
return false;
// Exception specs are not allowed in typedefs. Complain, but add it
// anyway.
- if (IsTypedefName && FTI.getExceptionSpecType())
+ if (IsTypedefName && FTI.getExceptionSpecType() && !LangOpts.CPlusPlus1z)
S.Diag(FTI.getExceptionSpecLocBeg(),
diag::err_exception_spec_in_typedef)
<< (D.getContext() == Declarator::AliasDeclContext ||
--- /dev/null
+// RUN: %clang_cc1 -std=c++1z -verify %s
+
+struct S;
+
+typedef void Nothrow() noexcept;
+typedef void Throw();
+
+Nothrow *a;
+Throw *b;
+Nothrow S::*c;
+Throw S::*d;
+
+void test() {
+ a = b; // expected-error {{assigning to 'Nothrow *' (aka 'void (*)() noexcept') from incompatible type 'Throw *' (aka 'void (*)()'): different exception specifications}}
+ b = a;
+ c = d; // expected-error {{assigning to 'Nothrow S::*' from incompatible type 'Throw S::*': different exception specifications}}
+ d = c;
+
+ // Function pointer conversions do not combine properly with qualification conversions.
+ // FIXME: This seems like a defect.
+ Nothrow *const *pa = b; // expected-error {{cannot initialize}}
+ Throw *const *pb = a; // expected-error {{cannot initialize}}
+ Nothrow *const S::*pc = d; // expected-error {{cannot initialize}}
+ Throw *const S::*pd = c; // expected-error {{cannot initialize}}
+}
+
+// ... The result is a pointer to the function.
+void f() noexcept;
+constexpr void (*p)() = &f;
+static_assert(f == p);
+
+struct S { void f() noexcept; };
+constexpr void (S::*q)() = &S::f;
+static_assert(q == &S::f);
+
+
+namespace std_example {
+ void (*p)() throw(int);
+ void (**pp)() noexcept = &p; // expected-error {{cannot initialize a variable of type 'void (**)() noexcept' with an rvalue of type 'void (**)() throw(int)'}}
+
+ struct S { typedef void (*p)(); operator p(); }; // expected-note {{candidate}}
+ void (*q)() noexcept = S(); // expected-error {{no viable conversion from 'std_example::S' to 'void (*)() noexcept'}}
+}
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s
void f0() &; // expected-error {{non-member function cannot have '&' qualifier}}
void f1() &&; // expected-error {{non-member function cannot have '&&' qualifier}}
};
pass<func_type_lvalue> pass0;
pass<func_type_lvalue> pass1;
+
+template<typename T, typename U> struct is_same { static const bool value = false; };
+template<typename T> struct is_same<T, T> { static const bool value = true; };
+constexpr bool cxx1z = __cplusplus > 201402L;
+
+void noexcept_true() noexcept(true);
+void noexcept_false() noexcept(false);
+using func_type_noexcept_true = wrap<decltype(noexcept_true)>;
+using func_type_noexcept_false = wrap<decltype(noexcept_false)>;
+static_assert(is_same<func_type_noexcept_false, func_type_noexcept_true>::value == !cxx1z, "");
+static_assert(is_same<func_type_noexcept_false::val, func_type_noexcept_true::val>::value == !cxx1z, "");
+static_assert(is_same<func_type_noexcept_false::ptr, func_type_noexcept_true::ptr>::value == !cxx1z, "");
+static_assert(is_same<func_type_noexcept_false::ref, func_type_noexcept_true::ref>::value == !cxx1z, "");
void f() throw(int);
};
void (A::*f)() throw (int);
- void (A::*g)() throw () = f; // expected-error {{is not superset of source}}
+ void (A::*g)() throw () = f;
+#if __cplusplus <= 201402L
+ // expected-error@-2 {{is not superset of source}}
+#else
+ // expected-error@-4 {{different exception specifications}}
+#endif
void (A::*g2)() throw () = 0;
void (A::*h)() throw (int, char) = f;
- void (A::*i)() throw () = &A::f; // expected-error {{is not superset of source}}
+ void (A::*i)() throw () = &A::f;
+#if __cplusplus <= 201402L
+ // expected-error@-2 {{is not superset of source}}
+#else
+ // expected-error@-4 {{different exception specifications}}
+#endif
void (A::*i2)() throw () = 0;
void (A::*j)() throw (int, char) = &A::f;
void x() {
- g2 = f; // expected-error {{is not superset}}
+ g2 = f;
+#if __cplusplus <= 201402L
+ // expected-error@-2 {{is not superset of source}}
+#else
+ // expected-error@-4 {{different exception specifications}}
+#endif
h = f;
- i2 = &A::f; // expected-error {{is not superset}}
+ i2 = &A::f;
+#if __cplusplus <= 201402L
+ // expected-error@-2 {{is not superset of source}}
+#else
+ // expected-error@-4 {{different exception specifications}}
+#endif
j = &A::f;
}
}
void f() throw(int, float);
void (*p)() throw(int) = &f; // expected-error {{target exception specification is not superset of source}}
void (*q)() throw(int);
- void (**pp)() throw() = &q; // expected-error {{exception specifications are not allowed}}
+ void (**pp)() throw() = &q;
+#if __cplusplus <= 201402L
+ // expected-error@-2 {{exception specifications are not allowed}}
+#else
+ // expected-error@-4 {{cannot initialize}}
+#endif
- void g(void() throw());
- void h() {
- g(f); // expected-error {{is not superset}}
- g(q); // expected-error {{is not superset}}
+ void g(void() throw()); // expected-note 0-2 {{no known conversion}}
+ void h() throw() {
+ g(f); // expected-error-re {{{{is not superset|no matching function}}}}
+ g(q); // expected-error-re {{{{is not superset|no matching function}}}}
}
// Prior to C++17, this is OK because the exception specification is not
// considered in this context. In C++17, we *do* perform an implicit
- // conversion (which performs initialization), but we convert to the type of
- // the template parameter, which does not include the exception specification.
+ // conversion (which performs initialization), and the exception specification
+ // is part of the type of the parameter, so this is invalid.
template<void() throw()> struct X {};
- X<&f> xp; // ok
+ X<&f> xp;
+#if __cplusplus > 201402L
+ // expected-error@-2 {{not implicitly convertible}}
+#endif
+
+ template<void() throw(int)> struct Y {};
+ Y<&h> yp; // ok
}
// dr93: na
friend dr125_A (::dr125_B::dr125_C)(); // ok
friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}}
// expected-error@-1 {{missing exception specification}}
-#if __cplusplus >= 201103L
- // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}}
-#endif
};
}
namespace dr294 { // dr294: no
void f() throw(int);
int main() {
- (void)static_cast<void (*)() throw()>(f); // FIXME: ill-formed
- (void)static_cast<void (*)() throw(int)>(f); // FIXME: ill-formed
+ (void)static_cast<void (*)() throw()>(f); // FIXME: ill-formed in C++14 and before
+#if __cplusplus > 201402L
+ // FIXME: expected-error@-2 {{not allowed}}
+ //
+ // Irony: the above is valid in C++17 and beyond, but that's exactly when
+ // we reject it. In C++14 and before, this is ill-formed because an
+ // exception-specification is not permitted in a type-id. In C++17, this is
+ // valid because it's the inverse of a standard conversion sequence
+ // containing a function pointer conversion.
+#endif
+ (void)static_cast<void (*)() throw(int)>(f); // FIXME: ill-formed in C++14 and before
- void (*p)() throw() = f; // expected-error {{not superset}}
+ void (*p)() throw() = f; // expected-error-re {{{{not superset|different exception specification}}}}
void (*q)() throw(int) = f;
}
}
--- /dev/null
+// RUN: %clang_cc1 -std=c++1z -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
+
+// In C++1z, we can put an exception-specification on any function declarator; the
+// corresponding paragraph from C++14 and before was deleted.
+// expected-no-diagnostics
+
+void f() noexcept;
+void (*fp)() noexcept;
+void (**fpp)() noexcept;
+void g(void (**pfa)() noexcept);
+void (**h())() noexcept;
+
+template<typename T> struct A {};
+template<void() noexcept> struct B {};
+A<void() noexcept> a;
+B<f> b;
+auto *p = new decltype(f)**;
// Pointer to function returning pointer to pointer to function with spec
void (**(*h())())() throw(int); // expected-error {{not allowed beyond a single}}
+ // FIXME: Missing a lot of negative tests, primarily type-ids in various places
+ // We fail to diagnose all of those.
}
namespace noex {
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++1z -verify %s
// A converted constant expression of type T is a core constant expression,
int nonconst = 8; // expected-note 3 {{here}}
template<E> struct T {};
T<e10> s10;
-// integral promotions, and
+// integral promotions,
enum class EE { EE32 = ' ', EE65 = 'A', EE1 = (short)1, EE5 = E5 };
-// integral conversions other than narrowing conversions
+// integral conversions other than narrowing conversions,
int b(unsigned n) {
switch (n) {
case E6:
// Note, conversions from integral or unscoped enumeration types to bool are
// integral conversions as well as boolean conversions.
+// FIXME: Per core issue 1407, this is not correct.
template<typename T, T v> struct Val { static constexpr T value = v; };
static_assert(Val<bool, E1>::value == 1, ""); // ok
static_assert(Val<bool, '\0'>::value == 0, ""); // ok
static_assert(Val<bool, U'\1'>::value == 1, ""); // ok
static_assert(Val<bool, E5>::value == 1, ""); // expected-error {{5, which cannot be narrowed to type 'bool'}}
+// function pointer conversions [C++17]
+void noexcept_false() noexcept(false);
+void noexcept_true() noexcept(true);
+Val<decltype(&noexcept_false), &noexcept_true> remove_noexcept;
+Val<decltype(&noexcept_true), &noexcept_false> add_noexcept;
+#if __cplusplus > 201402L
+// expected-error@-2 {{value of type 'void (*)() noexcept(false)' is not implicitly convertible to 'void (*)() noexcept'}}
+#endif
+
// (no other conversions are permitted)
using Int = A<1.0>; // expected-error {{conversion from 'double' to 'unsigned char' is not allowed in a converted constant expression}}
enum B : bool {
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify
+// RUN: %clang_cc1 -fsyntax-only -std=c++1z %s -verify
void test_conversion() {
int (*fp1)(int) = [](int x) { return x + 1; };
volatile const auto lambda2 = [](int x) { }; // expected-note{{but method is not marked volatile}}
void (*fp4)(int) = lambda2; // expected-error{{no viable conversion}}
+
+ void (*fp5)(int) noexcept = [](int x) { };
+#if __cplusplus > 201402L
+ // expected-error@-2 {{no viable}} expected-note@-2 {{candidate}}
+ void (*fp5a)(int) noexcept = [](auto x) { };
+ // expected-error@-1 {{no viable}} expected-note@-1 {{candidate}}
+ void (*fp5b)(int) noexcept = [](auto x) noexcept { };
+#endif
+ void (*fp6)(int) noexcept = [](int x) noexcept { };
}
void test_no_conversion() {
--- /dev/null
+// RUN: %clang_cc1 -std=c++1z -verify %s
+
+void f(void() noexcept); // expected-note {{no known conversion from 'void ()' to 'void (*)() noexcept'}}
+void f(void()) = delete; // expected-note {{explicitly deleted}}
+
+void g();
+void h() noexcept;
+
+void test() {
+ f(g); // expected-error {{call to deleted}}
+ f(h);
+}
-// RUN: %clang_cc1 -fsyntax-only %s
+// 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
-template<typename T> T f0(T);
-int f0(int);
+template<typename T> T f0(T) NOEXCEPT;
+int f0(int) NOEXCEPT;
// -- an object or reference being initialized
struct S {
// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
namespace PR8598 {
template<class T> struct identity { typedef T type; };
fun(&A::x);
}
}
+
+#if __cplusplus > 201402L
+namespace noexcept_conversion {
+ template<typename R> void foo(R());
+ template<typename R> void bar(R()) = delete;
+ template<typename R> void bar(R() noexcept) {}
+ void f() throw() {
+ foo(&f);
+ 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}}
+ void g() {
+ baz(f); // expected-error {{no match}}
+ }
+
+ 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}}
+}
+#else
+// expected-no-diagnostics
+#endif
--- /dev/null
+// RUN: %clang_cc1 -std=c++1z -verify %s
+
+template<typename T, bool B> using Fn = T () noexcept(B);
+
+// - If the original A is a function pointer type, A can be "pointer to
+// function" even if the deduced A is "pointer to noexcept function".
+struct A {
+ template<typename T> operator Fn<T, false>*(); // expected-note {{candidate}}
+};
+struct B {
+ template<typename T> operator Fn<T, true>*();
+};
+void (*p1)() = A();
+void (*p2)() = B();
+void (*p3)() noexcept = A(); // expected-error {{no viable conversion}}
+void (*p4)() noexcept = B();
+
+// - If the original A is a pointer to member function type, A can be "pointer
+// to member of type function" even if the deduced A is "pointer to member of
+// type noexcept function".
+struct C {
+ template<typename T> operator Fn<T, false> A::*(); // expected-note {{candidate}}
+};
+struct D {
+ template<typename T> operator Fn<T, true> A::*();
+};
+void (A::*q1)() = C();
+void (A::*q2)() = D();
+void (A::*q3)() noexcept = C(); // expected-error {{no viable conversion}}
+void (A::*q4)() noexcept = D();
+
+// There is no corresponding rule for references.
+// FIXME: This seems like a defect.
+struct E {
+ template<typename T> operator Fn<T, false>&(); // expected-note {{candidate}}
+};
+struct F {
+ template<typename T> operator Fn<T, true>&(); // expected-note {{candidate}}
+};
+void (&r1)() = E();
+void (&r2)() = F(); // expected-error {{no viable conversion}}
+void (&r3)() noexcept = E(); // expected-error {{no viable conversion}}
+void (&r4)() noexcept = F();
Y &operator=(Y&&) = default;
~Y() = default;
};
- Y::Y() = default; // expected-error {{definition of explicitly defaulted}}
- Y::Y(const Y&) = default; // expected-error {{definition of explicitly defaulted}}
- Y::Y(Y&&) = default; // expected-error {{definition of explicitly defaulted}}
- Y &Y::operator=(const Y&) = default; // expected-error {{definition of explicitly defaulted}}
- Y &Y::operator=(Y&&) = default; // expected-error {{definition of explicitly defaulted}}
+ Y::Y() noexcept = default; // expected-error {{definition of explicitly defaulted}}
+ Y::Y(const Y&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
+ Y::Y(Y&&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
+ Y &Y::operator=(const Y&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
+ Y &Y::operator=(Y&&) noexcept = default; // expected-error {{definition of explicitly defaulted}}
Y::~Y() = default; // expected-error {{definition of explicitly defaulted}}
}
#include "Inputs/register.h"
-void f() throw();
-void g() throw(int);
-void h() throw(...);
+void g() throw();
+void h() throw(int);
+void i() throw(...);
#if __cplusplus >= 201103L
// expected-warning@-4 {{dynamic exception specifications are deprecated}} expected-note@-4 {{use 'noexcept' instead}}
// expected-warning@-4 {{dynamic exception specifications are deprecated}} expected-note@-4 {{use 'noexcept(false)' instead}}