From: Richard Smith Date: Sun, 16 Oct 2016 17:54:23 +0000 (+0000) Subject: P0012R1: Make exception specifications be part of the type system. This X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3c4f8d2e96c4b83108bfc48bfdaf2b6d5ebbd6bf;p=platform%2Fupstream%2Fllvm.git P0012R1: Make exception specifications be part of the type system. This implements the bulk of the change (modifying the type system to include exception specifications), but not all the details just yet. llvm-svn: 284337 --- diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index fb1b50f..e9351a9 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3364,9 +3364,15 @@ public: return reinterpret_cast(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; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b92ca3d..e274b46 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1644,7 +1644,8 @@ def err_init_conversion_failed : Error< "%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">; @@ -3278,7 +3279,8 @@ def note_ovl_candidate : Note<"candidate " "%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">; @@ -6101,7 +6103,8 @@ def note_hidden_overloaded_virtual_declared_here : Note< "%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, DefaultIgnore; @@ -6343,7 +6346,8 @@ def err_typecheck_convert_incompatible : Error< "%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 " diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 1c2eb92..b3447c5 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -62,7 +62,7 @@ namespace clang { 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) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a50dd66..6e0f379 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2373,7 +2373,7 @@ public: 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); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 709de43..34eced3 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3193,6 +3193,34 @@ ASTContext::getCanonicalFunctionResultType(QualType ResultType) const { 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()) + 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 ArgArray, const FunctionProtoType::ExtProtoInfo &EPI) const { @@ -3209,10 +3237,14 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, 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; @@ -3228,7 +3260,45 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, 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); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 113974c..2601fa4 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2717,8 +2717,9 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, 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(); @@ -2757,6 +2758,19 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, 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(getExtParameterInfosBuffer()); @@ -2801,29 +2815,28 @@ FunctionProtoType::getNoexceptSpec(const ASTContext &ctx) const { 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()) - 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 { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index efdf8a3..54b56b3 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2933,10 +2933,20 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, } 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: @@ -2951,10 +2961,10 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, (New->getTypeSourceInfo() ? New->getTypeSourceInfo()->getType()->castAs() : 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); @@ -3092,7 +3102,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, // 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(); const FunctionType *OldTypeForComparison = Context.adjustFunctionType(OldType, OldTypeInfo.withNoReturn(true)); OldQTypeForComparison = QualType(OldTypeForComparison, 0); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 93e626f..f86b793 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -659,9 +659,6 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Invalid = true; } - if (CheckEquivalentExceptionSpec(Old, New)) - Invalid = true; - return Invalid; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index f8e75b2..a81ef51 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -130,6 +130,11 @@ bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) { /// 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()) T = PT->getPointeeType(); else if (const MemberPointerType *PT = T->getAs()) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ec7f7bc..206a704 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7288,7 +7288,7 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType) { return Sema::IncompatiblePointer; } if (!S.getLangOpts().CPlusPlus && - S.IsNoReturnConversion(ltrans, rtrans, ltrans)) + S.IsFunctionConversion(ltrans, rtrans, ltrans)) return Sema::IncompatiblePointer; return ConvTy; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4de7fc3..56f5934 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -3610,7 +3610,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // 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)) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 082a6f9..460e53f 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -150,7 +150,7 @@ static const char* GetImplicitConversionName(ImplicitConversionKind Kind) { "Lvalue-to-rvalue", "Array-to-pointer", "Function-to-pointer", - "Noreturn adjustment", + "Function pointer conversion", "Qualification", "Integral promotion", "Floating point promotion", @@ -1390,13 +1390,15 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, } /// \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 @@ -1425,11 +1427,37 @@ bool Sema::IsNoReturnConversion(QualType FromType, QualType ToType, return false; } - const FunctionType *FromFn = cast(CanFrom); - FunctionType::ExtInfo EInfo = FromFn->getExtInfo(); - if (!EInfo.getNoReturn()) return false; + const auto *FromFn = cast(CanFrom); + FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); + + const auto *ToFn = dyn_cast(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(FromFn)) { + const auto *ToFPT = dyn_cast(ToFn); + if (FromFPT->isNothrow(Context) && !ToFPT->isNothrow(Context)) { + FromFn = cast( + 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; @@ -1534,7 +1562,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, 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()) @@ -1727,9 +1755,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // 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)) { @@ -2615,7 +2644,8 @@ enum { 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 @@ -2715,6 +2745,16 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, return; } + // Handle exception specification differences on canonical type (in C++17 + // onwards). + if (cast(FromFunction->getCanonicalTypeUnqualified()) + ->isNothrow(Context) != + cast(ToFunction->getCanonicalTypeUnqualified()) + ->isNothrow(Context)) { + PDiag << ft_noexcept; + return; + } + // Unable to find a difference, so add no extra info. PDiag << ft_default; } @@ -5096,7 +5136,7 @@ static bool CheckConvertedConstantConversions(Sema &S, // 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; @@ -10428,7 +10468,7 @@ private: 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 diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 4f76a02..2e4b9ca 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -956,9 +956,9 @@ bool Sema::isSameOrCompatibleFunctionType(CanQualType Param, 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. @@ -2757,18 +2757,17 @@ CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, } // - 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; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index cfbfc3b..b1daab4 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -4142,7 +4142,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // 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 || diff --git a/clang/test/CXX/conv/conv.fctptr/p1.cpp b/clang/test/CXX/conv/conv.fctptr/p1.cpp new file mode 100644 index 0000000..810c7f5 --- /dev/null +++ b/clang/test/CXX/conv/conv.fctptr/p1.cpp @@ -0,0 +1,43 @@ +// 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'}} +} diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp index cec747e..d93cc8b 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp @@ -1,4 +1,5 @@ // 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}} @@ -58,3 +59,16 @@ template struct pass { }; pass pass0; pass pass1; + +template struct is_same { static const bool value = false; }; +template struct is_same { 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; +using func_type_noexcept_false = wrap; +static_assert(is_same::value == !cxx1z, ""); +static_assert(is_same::value == !cxx1z, ""); +static_assert(is_same::value == !cxx1z, ""); +static_assert(is_same::value == !cxx1z, ""); diff --git a/clang/test/CXX/drs/dr0xx.cpp b/clang/test/CXX/drs/dr0xx.cpp index d598cfa..2e895ed 100644 --- a/clang/test/CXX/drs/dr0xx.cpp +++ b/clang/test/CXX/drs/dr0xx.cpp @@ -279,16 +279,36 @@ namespace dr25 { // dr25: yes 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; } } @@ -997,20 +1017,31 @@ namespace dr92 { // FIXME: Issue is still open. 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 struct X {}; - X<&f> xp; // ok + X<&f> xp; +#if __cplusplus > 201402L + // expected-error@-2 {{not implicitly convertible}} +#endif + + template struct Y {}; + Y<&h> yp; // ok } // dr93: na diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index 60d6cca..58626c99 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -235,9 +235,6 @@ namespace dr125 { 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 }; } diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index 7372ecb..0fa2500 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -984,10 +984,19 @@ namespace dr289 { // dr289: yes namespace dr294 { // dr294: no void f() throw(int); int main() { - (void)static_cast(f); // FIXME: ill-formed - (void)static_cast(f); // FIXME: ill-formed + (void)static_cast(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(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; } } diff --git a/clang/test/CXX/except/except.spec/p2-places-1z.cpp b/clang/test/CXX/except/except.spec/p2-places-1z.cpp new file mode 100644 index 0000000..619ea33 --- /dev/null +++ b/clang/test/CXX/except/except.spec/p2-places-1z.cpp @@ -0,0 +1,17 @@ +// 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 struct A {}; +template struct B {}; +A a; +B b; +auto *p = new decltype(f)**; diff --git a/clang/test/CXX/except/except.spec/p2-places.cpp b/clang/test/CXX/except/except.spec/p2-places.cpp index 67647fb..ea842af 100644 --- a/clang/test/CXX/except/except.spec/p2-places.cpp +++ b/clang/test/CXX/except/except.spec/p2-places.cpp @@ -37,6 +37,8 @@ namespace dyn { // 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 { diff --git a/clang/test/CXX/expr/expr.const/p3-0x.cpp b/clang/test/CXX/expr/expr.const/p3-0x.cpp index d9d8485..731e0c3 100644 --- a/clang/test/CXX/expr/expr.const/p3-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p3-0x.cpp @@ -1,4 +1,5 @@ // 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}} @@ -40,10 +41,10 @@ const E e10 = E10; template struct T {}; T 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: @@ -74,12 +75,22 @@ using Int = A<-3>; // expected-error {{template argument evaluates to -3, which // 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 struct Val { static constexpr T value = v; }; static_assert(Val::value == 1, ""); // ok static_assert(Val::value == 0, ""); // ok static_assert(Val::value == 1, ""); // ok static_assert(Val::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 remove_noexcept; +Val 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 { diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p6.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p6.cpp index 8b43cef..90a3aec 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p6.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p6.cpp @@ -1,4 +1,5 @@ // 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; }; @@ -9,6 +10,15 @@ void test_conversion() { 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() { diff --git a/clang/test/CXX/over/over.match/over.match.best/over.best.ics/over.ics.scs/p3.cpp b/clang/test/CXX/over/over.match/over.match.best/over.best.ics/over.ics.scs/p3.cpp new file mode 100644 index 0000000..df2fc2c --- /dev/null +++ b/clang/test/CXX/over/over.match/over.match.best/over.best.ics/over.ics.scs/p3.cpp @@ -0,0 +1,12 @@ +// 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); +} diff --git a/clang/test/CXX/over/over.over/p1.cpp b/clang/test/CXX/over/over.over/p1.cpp index 10c60da..430d1a2 100644 --- a/clang/test/CXX/over/over.over/p1.cpp +++ b/clang/test/CXX/over/over.over/p1.cpp @@ -1,7 +1,9 @@ -// 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 T f0(T); -int f0(int); +template T f0(T) NOEXCEPT; +int f0(int) NOEXCEPT; // -- an object or reference being initialized struct S { 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 132d618..cf9003b 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 @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s namespace PR8598 { template struct identity { typedef T type; }; @@ -19,3 +19,28 @@ namespace PR12132 { fun(&A::x); } } + +#if __cplusplus > 201402L +namespace noexcept_conversion { + template void foo(R()); + template void bar(R()) = delete; + template 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 void baz(R(&)()); // expected-note {{does not match adjusted type}} + void g() { + baz(f); // expected-error {{no match}} + } + + 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}} +} +#else +// expected-no-diagnostics +#endif 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 new file mode 100644 index 0000000..ed7b000 --- /dev/null +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p5.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +template 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 operator Fn*(); // expected-note {{candidate}} +}; +struct B { + template operator Fn*(); +}; +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 operator Fn A::*(); // expected-note {{candidate}} +}; +struct D { + template operator Fn 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 operator Fn&(); // expected-note {{candidate}} +}; +struct F { + template operator Fn&(); // 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(); diff --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp index 7ec9726..c9c9fce 100644 --- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp @@ -142,11 +142,11 @@ namespace PR13527 { 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}} } diff --git a/clang/test/SemaCXX/deprecated.cpp b/clang/test/SemaCXX/deprecated.cpp index 4ce0589..7a17237 100644 --- a/clang/test/SemaCXX/deprecated.cpp +++ b/clang/test/SemaCXX/deprecated.cpp @@ -7,9 +7,9 @@ #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}}