Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3430,8 +3430,13 @@ CastKind &Kind, CXXCastPath &BasePath, bool IgnoreBaseAccess); - bool IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle, bool &ObjCLifetimeConversion); + enum QualificationConversionKind { + QCK_NotQualificationConversion = 0, // Not a conversion + QCK_QualificationConversion = 1 << 0, // Is a qualification + QCK_IncludesObjCLifetimeConversion = 1 << 1, // ... with ObjC lifetime + }; + QualificationConversionKind + TryQualificationConversion(QualType FromType, QualType ToType, bool CStyle); bool IsFunctionConversion(QualType FromType, QualType ToType, QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); Index: clang/lib/Sema/SemaExceptionSpec.cpp =================================================================== --- clang/lib/Sema/SemaExceptionSpec.cpp +++ clang/lib/Sema/SemaExceptionSpec.cpp @@ -700,12 +700,10 @@ // that can be converted to T by one or more of // -- a qualification conversion // -- a function pointer conversion - bool LifetimeConv; QualType Result; // FIXME: Should we treat the exception as catchable if a lifetime // conversion is required? - if (IsQualificationConversion(ExceptionType, HandlerType, false, - LifetimeConv) || + if (TryQualificationConversion(ExceptionType, HandlerType, false) || IsFunctionConversion(ExceptionType, HandlerType, Result)) return true; Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -1956,15 +1956,15 @@ // The third conversion can be a function pointer conversion or a // qualification conversion (C++ [conv.fctptr], [conv.qual]). - bool ObjCLifetimeConversion; if (S.IsFunctionConversion(FromType, ToType, FromType)) { // Function pointer conversions (removing 'noexcept') including removal of // 'noreturn' (Clang extension). SCS.Third = ICK_Function_Conversion; - } else if (S.IsQualificationConversion(FromType, ToType, CStyle, - ObjCLifetimeConversion)) { + } else if (auto qck = + S.TryQualificationConversion(FromType, ToType, CStyle)) { SCS.Third = ICK_Qualification; - SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion; + SCS.QualificationIncludesObjCLifetime = + bool(qck & Sema::QCK_IncludesObjCLifetimeConversion); FromType = ToType; } else { // No conversion required @@ -3189,12 +3189,13 @@ /// Specifically, check whether any change between the qualifiers of \p /// FromType and \p ToType is permissible, given knowledge about whether every /// outer layer is const-qualified. -static bool isQualificationConversionStep(QualType FromType, QualType ToType, - bool CStyle, bool IsTopLevel, - bool &PreviousToQualsIncludeConst, - bool &ObjCLifetimeConversion) { +static Sema::QualificationConversionKind +tryQualificationConversionStep(QualType FromType, QualType ToType, bool CStyle, + bool IsTopLevel, + bool &PreviousToQualsIncludeConst) { Qualifiers FromQuals = FromType.getQualifiers(); Qualifiers ToQuals = ToType.getQualifiers(); + Sema::QualificationConversionKind result = Sema::QCK_QualificationConversion; // Ignore __unaligned qualifier if this type is void. if (ToType.getUnqualifiedType()->isVoidType()) @@ -3205,13 +3206,14 @@ if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime()) { if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) { if (isNonTrivialObjCLifetimeConversion(FromQuals, ToQuals)) - ObjCLifetimeConversion = true; + result = Sema::QualificationConversionKind( + result | Sema::QCK_IncludesObjCLifetimeConversion); FromQuals.removeObjCLifetime(); ToQuals.removeObjCLifetime(); } else { // Qualification conversions cannot cast between different // Objective-C lifetime qualifiers. - return false; + return Sema::QCK_NotQualificationConversion; } } @@ -3225,7 +3227,7 @@ // -- for every j > 0, if const is in cv 1,j then const is in cv // 2,j, and similarly for volatile. if (!CStyle && !ToQuals.compatiblyIncludes(FromQuals)) - return false; + return Sema::QCK_NotQualificationConversion; // If address spaces mismatch: // - in top level it is only valid to convert to addr space that is a @@ -3236,50 +3238,52 @@ (!IsTopLevel || !(ToQuals.isAddressSpaceSupersetOf(FromQuals) || (CStyle && FromQuals.isAddressSpaceSupersetOf(ToQuals))))) - return false; + return Sema::QCK_NotQualificationConversion; // -- if the cv 1,j and cv 2,j are different, then const is in // every cv for 0 < k < j. if (!CStyle && FromQuals.getCVRQualifiers() != ToQuals.getCVRQualifiers() && !PreviousToQualsIncludeConst) - return false; + return Sema::QCK_NotQualificationConversion; // Keep track of whether all prior cv-qualifiers in the "to" type // include const. PreviousToQualsIncludeConst = PreviousToQualsIncludeConst && ToQuals.hasConst(); - return true; + + return result; } -/// IsQualificationConversion - Determines whether the conversion from +/// TryQualificationConversion - Determines whether the conversion from /// an rvalue of type FromType to ToType is a qualification conversion /// (C++ 4.4). /// -/// \param ObjCLifetimeConversion Output parameter that will be set to indicate -/// when the qualification conversion involves a change in the Objective-C -/// object lifetime. -bool -Sema::IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle, bool &ObjCLifetimeConversion) { +/// \result An enum of bits describing additional features of the qualification +/// conversion. Contextually convertable to a bool. +Sema::QualificationConversionKind +Sema::TryQualificationConversion(QualType FromType, QualType ToType, + bool CStyle) { FromType = Context.getCanonicalType(FromType); ToType = Context.getCanonicalType(ToType); - ObjCLifetimeConversion = false; - // If FromType and ToType are the same type, this is not a // qualification conversion. if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType()) - return false; + return QCK_NotQualificationConversion; // (C++ 4.4p4): // A conversion can add cv-qualifiers at levels other than the first // in multi-level pointers, subject to the following rules: [...] bool PreviousToQualsIncludeConst = true; bool UnwrappedAnyPointer = false; + QualificationConversionKind result = QCK_QualificationConversion; while (Context.UnwrapSimilarTypes(FromType, ToType)) { - if (!isQualificationConversionStep( + if (auto qck = tryQualificationConversionStep( FromType, ToType, CStyle, !UnwrappedAnyPointer, - PreviousToQualsIncludeConst, ObjCLifetimeConversion)) - return false; + PreviousToQualsIncludeConst)) { + result = QualificationConversionKind(result | qck); + } else { + return QCK_NotQualificationConversion; + } UnwrappedAnyPointer = true; } @@ -3288,7 +3292,11 @@ // of times. If we unwrapped any pointers, and if FromType and // ToType have the same unqualified type (since we checked // qualifiers above), then this is a qualification conversion. - return UnwrappedAnyPointer && Context.hasSameUnqualifiedType(FromType,ToType); + if (!(UnwrappedAnyPointer && + Context.hasSameUnqualifiedType(FromType, ToType))) + return QCK_NotQualificationConversion; + + return result; } /// - Determine whether this is a conversion from a scalar type to an @@ -4582,17 +4590,16 @@ // If we find a qualifier mismatch, the types are not reference-compatible, // but are still be reference-related if they're similar. - bool ObjCLifetimeConversion = false; - if (!isQualificationConversionStep(T2, T1, /*CStyle=*/false, TopLevel, - PreviousToQualsIncludeConst, - ObjCLifetimeConversion)) + if (auto qck = tryQualificationConversionStep( + T2, T1, /*CStyle=*/false, TopLevel, PreviousToQualsIncludeConst)) { + // FIXME: Should we be tracking this for inner levels? + if (bool(qck & QCK_IncludesObjCLifetimeConversion)) + Conv |= ReferenceConversions::ObjCLifetime; + } else { return (ConvertedReferent || Context.hasSimilarType(T1, T2)) ? Ref_Related : Ref_Incompatible; - - // FIXME: Should we track this for any level other than the first? - if (ObjCLifetimeConversion) - Conv |= ReferenceConversions::ObjCLifetime; + } TopLevel = false; } while (Context.UnwrapSimilarTypes(T1, T2)); @@ -7228,9 +7235,7 @@ return true; // Allow qualification conversions. - bool ObjCLifetimeConversion; - if (S.IsQualificationConversion(ConvType, ToNonRefType, /*CStyle*/false, - ObjCLifetimeConversion)) + if (S.TryQualificationConversion(ConvType, ToNonRefType, /*CStyle*/ false)) return true; // If we're not allowed to consider Objective-C pointer conversions, Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -6305,10 +6305,8 @@ (EvalResult.Val.isMemberPointer() && !EvalResult.Val.getMemberPointerDecl())) { // If our expression has an appropriate type, we've succeeded. - bool ObjCLifetimeConversion; if (S.Context.hasSameUnqualifiedType(Arg->getType(), ParamType) || - S.IsQualificationConversion(Arg->getType(), ParamType, false, - ObjCLifetimeConversion)) + S.TryQualificationConversion(Arg->getType(), ParamType, false)) return NPV_NullPointer; // The types didn't match, but we know we got a null pointer; complain, @@ -6342,11 +6340,9 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter( Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn, Expr *Arg, QualType ArgType) { - bool ObjCLifetimeConversion; if (ParamType->isPointerType() && !ParamType->castAs()->getPointeeType()->isFunctionType() && - S.IsQualificationConversion(ArgType, ParamType, false, - ObjCLifetimeConversion)) { + S.TryQualificationConversion(ArgType, ParamType, false)) { // For pointer-to-object types, qualification conversions are // permitted. } else { @@ -6666,7 +6662,6 @@ bool Invalid = false; Expr *Arg = ResultArg; - bool ObjCLifetimeConversion; // C++ [temp.arg.nontype]p1: // @@ -6738,9 +6733,8 @@ break; } - if (S.IsQualificationConversion(ResultArg->getType(), - ParamType.getNonReferenceType(), false, - ObjCLifetimeConversion)) { + if (S.TryQualificationConversion(ResultArg->getType(), + ParamType.getNonReferenceType(), false)) { ResultArg = S.ImpCastExprToType(ResultArg, ParamType, CK_NoOp, ResultArg->getValueKind()) .get(); Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3479,11 +3479,9 @@ // // Also allow conversions which merely strip __attribute__((noreturn)) from // function types (recursively). - bool ObjCLifetimeConversion = false; QualType ResultTy; if ((A->isAnyPointerType() || A->isMemberPointerType()) && - (S.IsQualificationConversion(A, DeducedA, false, - ObjCLifetimeConversion) || + (S.TryQualificationConversion(A, DeducedA, false) || S.IsFunctionConversion(A, DeducedA, ResultTy))) return Sema::TDK_Success;