From 4c3fc9b38d3723f73e4ded594cebf38c76f91d93 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 18 Jan 2012 05:21:49 +0000 Subject: [PATCH] Move narrowing conversion detection code from SemaInit to SemaOverload, ready for it to be used in converted constant expression checking, and fix a couple of issues: - Conversion operators implicitly invoked prior to the narrowing conversion were not being correctly handled when determining whether a constant value was narrowed. - For conversions from floating-point to integral types, the diagnostic text incorrectly always claimed that the source expression was not a constant expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148381 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 5 + include/clang/Basic/DiagnosticSemaKinds.td | 5 + include/clang/Sema/Overload.h | 19 ++ lib/AST/ExprConstant.cpp | 65 +++-- lib/Sema/SemaInit.cpp | 259 ++++++------------ lib/Sema/SemaOverload.cpp | 157 +++++++++++ .../dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp | 26 +- 7 files changed, 326 insertions(+), 210 deletions(-) diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index b9fd02f77e..04ae222b70 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -420,6 +420,11 @@ public: bool isEvaluated = true) const; bool isIntegerConstantExpr(ASTContext &Ctx, SourceLocation *Loc = 0) const; + /// isCXX11ConstantExpr - Return true if this expression is a constant + /// expression in C++11. Can only be used in C++. + bool isCXX11ConstantExpr(ASTContext &Ctx, APValue *Result = 0, + SourceLocation *Loc = 0) const; + /// isConstantInitializer - Returns true if this expression can be emitted to /// IR as a constant, and thus can be used as a constant initializer in C. bool isConstantInitializer(ASTContext &Ctx, bool ForRef) const; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9b741101f4..2593697aec 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2927,11 +2927,16 @@ def warn_cxx98_compat_empty_scalar_initializer : Warning< def err_illegal_initializer : Error< "illegal initializer (only variables can be initialized)">; def err_illegal_initializer_type : Error<"illegal initializer type %0">; +def err_init_list_type_narrowing : Error< + "type %0 cannot be narrowed to %1 in initializer list">; def err_init_list_variable_narrowing : Error< "non-constant-expression cannot be narrowed from type %0 to %1 in " "initializer list">; def err_init_list_constant_narrowing : Error< "constant expression evaluates to %0 which cannot be narrowed to type %1">; +def warn_init_list_type_narrowing : Warning< + "type %0 cannot be narrowed to %1 in initializer list in C++11">, + InGroup, DefaultIgnore; def warn_init_list_variable_narrowing : Warning< "non-constant-expression cannot be narrowed from type %0 to %1 in " "initializer list in C++11">, diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 3fcc56790c..9a3397d235 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -112,6 +112,23 @@ namespace clang { ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); + /// NarrowingKind - The kind of narrowing conversion being performed by a + /// standard conversion sequence according to C++11 [dcl.init.list]p7. + enum NarrowingKind { + /// Not a narrowing conversion. + NK_Not_Narrowing, + + /// A narrowing conversion by virtue of the source and destination types. + NK_Type_Narrowing, + + /// A narrowing conversion, because a constant expression got narrowed. + NK_Constant_Narrowing, + + /// A narrowing conversion, because a non-constant-expression variable might + /// have got narrowed. + NK_Variable_Narrowing + }; + /// StandardConversionSequence - represents a standard conversion /// sequence (C++ 13.3.3.1.1). A standard conversion sequence /// contains between zero and three conversions. If a particular @@ -218,6 +235,8 @@ namespace clang { } ImplicitConversionRank getRank() const; + NarrowingKind isNarrowing(ASTContext &Context, const Expr *Converted, + APValue &ConstantValue) const; bool isPointerConversionToBool() const; bool isPointerConversionToVoidPointer(ASTContext& Context) const; void DebugPrint() const; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index fc3c5aac01..5d61406959 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1756,18 +1756,16 @@ static bool CheckTrivialDefaultConstructor(EvalInfo &Info, SourceLocation Loc, if (!CD->isTrivial() || !CD->isDefaultConstructor()) return false; - if (!CD->isConstexpr()) { + // Value-initialization does not call a trivial default constructor, so such a + // call is a core constant expression whether or not the constructor is + // constexpr. + if (!CD->isConstexpr() && !IsValueInitialization) { if (Info.getLangOpts().CPlusPlus0x) { - // Value-initialization does not call a trivial default constructor, so - // such a call is a core constant expression whether or not the - // constructor is constexpr. - if (!IsValueInitialization) { - // FIXME: If DiagDecl is an implicitly-declared special member function, - // we should be much more explicit about why it's not constexpr. - Info.CCEDiag(Loc, diag::note_constexpr_invalid_function, 1) - << /*IsConstexpr*/0 << /*IsConstructor*/1 << CD; - Info.Note(CD->getLocation(), diag::note_declared_at); - } + // FIXME: If DiagDecl is an implicitly-declared special member function, + // we should be much more explicit about why it's not constexpr. + Info.CCEDiag(Loc, diag::note_constexpr_invalid_function, 1) + << /*IsConstexpr*/0 << /*IsConstructor*/1 << CD; + Info.Note(CD->getLocation(), diag::note_declared_at); } else { Info.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr); } @@ -5944,24 +5942,12 @@ static bool EvaluateCPlusPlus11IntegralConstantExpr(ASTContext &Ctx, return false; } - Expr::EvalResult Result; - llvm::SmallVector Diags; - Result.Diag = &Diags; - EvalInfo Info(Ctx, Result); - - bool IsICE = EvaluateAsRValue(Info, E, Result.Val); - if (!Diags.empty()) { - IsICE = false; - if (Loc) *Loc = Diags[0].first; - } else if (!IsICE && Loc) { - *Loc = E->getExprLoc(); - } - - if (!IsICE) + APValue Result; + if (!E->isCXX11ConstantExpr(Ctx, &Result, Loc)) return false; - assert(Result.Val.isInt() && "pointer cast to int is not an ICE"); - if (Value) *Value = Result.Val.getInt(); + assert(Result.isInt() && "pointer cast to int is not an ICE"); + if (Value) *Value = Result.getInt(); return true; } @@ -5988,3 +5974,28 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Value, ASTContext &Ctx, llvm_unreachable("ICE cannot be evaluated!"); return true; } + +bool Expr::isCXX11ConstantExpr(ASTContext &Ctx, APValue *Result, + SourceLocation *Loc) const { + // We support this checking in C++98 mode in order to diagnose compatibility + // issues. + assert(Ctx.getLangOptions().CPlusPlus); + + Expr::EvalStatus Status; + llvm::SmallVector Diags; + Status.Diag = &Diags; + EvalInfo Info(Ctx, Status); + + APValue Scratch; + bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch); + + if (!Diags.empty()) { + IsConstExpr = false; + if (Loc) *Loc = Diags[0].first; + } else if (!IsConstExpr) { + // FIXME: This shouldn't happen. + if (Loc) *Loc = getExprLoc(); + } + + return IsConstExpr; +} diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 2f9b33edeb..bd9701ef37 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2464,164 +2464,6 @@ bool InitializationSequence::isConstructorInitialization() const { return !Steps.empty() && Steps.back().Kind == SK_ConstructorInitialization; } -bool InitializationSequence::endsWithNarrowing(ASTContext &Ctx, - const Expr *Initializer, - bool *isInitializerConstant, - APValue *ConstantValue) const { - if (Steps.empty() || Initializer->isValueDependent()) - return false; - - const Step &LastStep = Steps.back(); - if (LastStep.Kind != SK_ConversionSequence) - return false; - - const ImplicitConversionSequence &ICS = *LastStep.ICS; - const StandardConversionSequence *SCS = NULL; - switch (ICS.getKind()) { - case ImplicitConversionSequence::StandardConversion: - SCS = &ICS.Standard; - break; - case ImplicitConversionSequence::UserDefinedConversion: - SCS = &ICS.UserDefined.After; - break; - case ImplicitConversionSequence::AmbiguousConversion: - case ImplicitConversionSequence::EllipsisConversion: - case ImplicitConversionSequence::BadConversion: - return false; - } - - // Check if SCS represents a narrowing conversion, according to C++0x - // [dcl.init.list]p7: - // - // A narrowing conversion is an implicit conversion ... - ImplicitConversionKind PossibleNarrowing = SCS->Second; - QualType FromType = SCS->getToType(0); - QualType ToType = SCS->getToType(1); - switch (PossibleNarrowing) { - // * from a floating-point type to an integer type, or - // - // * from an integer type or unscoped enumeration type to a floating-point - // type, except where the source is a constant expression and the actual - // value after conversion will fit into the target type and will produce - // the original value when converted back to the original type, or - case ICK_Floating_Integral: - if (FromType->isRealFloatingType() && ToType->isIntegralType(Ctx)) { - *isInitializerConstant = false; - return true; - } else if (FromType->isIntegralType(Ctx) && ToType->isRealFloatingType()) { - llvm::APSInt IntConstantValue; - if (Initializer && - Initializer->isIntegerConstantExpr(IntConstantValue, Ctx)) { - // Convert the integer to the floating type. - llvm::APFloat Result(Ctx.getFloatTypeSemantics(ToType)); - Result.convertFromAPInt(IntConstantValue, IntConstantValue.isSigned(), - llvm::APFloat::rmNearestTiesToEven); - // And back. - llvm::APSInt ConvertedValue = IntConstantValue; - bool ignored; - Result.convertToInteger(ConvertedValue, - llvm::APFloat::rmTowardZero, &ignored); - // If the resulting value is different, this was a narrowing conversion. - if (IntConstantValue != ConvertedValue) { - *isInitializerConstant = true; - *ConstantValue = APValue(IntConstantValue); - return true; - } - } else { - // Variables are always narrowings. - *isInitializerConstant = false; - return true; - } - } - return false; - - // * from long double to double or float, or from double to float, except - // where the source is a constant expression and the actual value after - // conversion is within the range of values that can be represented (even - // if it cannot be represented exactly), or - case ICK_Floating_Conversion: - if (1 == Ctx.getFloatingTypeOrder(FromType, ToType)) { - // FromType is larger than ToType. - Expr::EvalResult InitializerValue; - // FIXME: Check whether Initializer is a constant expression according - // to C++0x [expr.const], rather than just whether it can be folded. - if (Initializer->EvaluateAsRValue(InitializerValue, Ctx) && - !InitializerValue.HasSideEffects && InitializerValue.Val.isFloat()) { - // Constant! (Except for FIXME above.) - llvm::APFloat FloatVal = InitializerValue.Val.getFloat(); - // Convert the source value into the target type. - bool ignored; - llvm::APFloat::opStatus ConvertStatus = FloatVal.convert( - Ctx.getFloatTypeSemantics(ToType), - llvm::APFloat::rmNearestTiesToEven, &ignored); - // If there was no overflow, the source value is within the range of - // values that can be represented. - if (ConvertStatus & llvm::APFloat::opOverflow) { - *isInitializerConstant = true; - *ConstantValue = InitializerValue.Val; - return true; - } - } else { - *isInitializerConstant = false; - return true; - } - } - return false; - - // * from an integer type or unscoped enumeration type to an integer type - // that cannot represent all the values of the original type, except where - // the source is a constant expression and the actual value after - // conversion will fit into the target type and will produce the original - // value when converted back to the original type. - case ICK_Boolean_Conversion: // Bools are integers too. - if (!FromType->isIntegralOrUnscopedEnumerationType()) { - // Boolean conversions can be from pointers and pointers to members - // [conv.bool], and those aren't considered narrowing conversions. - return false; - } // Otherwise, fall through to the integral case. - case ICK_Integral_Conversion: { - assert(FromType->isIntegralOrUnscopedEnumerationType()); - assert(ToType->isIntegralOrUnscopedEnumerationType()); - const bool FromSigned = FromType->isSignedIntegerOrEnumerationType(); - const unsigned FromWidth = Ctx.getIntWidth(FromType); - const bool ToSigned = ToType->isSignedIntegerOrEnumerationType(); - const unsigned ToWidth = Ctx.getIntWidth(ToType); - - if (FromWidth > ToWidth || - (FromWidth == ToWidth && FromSigned != ToSigned)) { - // Not all values of FromType can be represented in ToType. - llvm::APSInt InitializerValue; - if (Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) { - *isInitializerConstant = true; - *ConstantValue = APValue(InitializerValue); - - // Add a bit to the InitializerValue so we don't have to worry about - // signed vs. unsigned comparisons. - InitializerValue = InitializerValue.extend( - InitializerValue.getBitWidth() + 1); - // Convert the initializer to and from the target width and signed-ness. - llvm::APSInt ConvertedValue = InitializerValue; - ConvertedValue = ConvertedValue.trunc(ToWidth); - ConvertedValue.setIsSigned(ToSigned); - ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth()); - ConvertedValue.setIsSigned(InitializerValue.isSigned()); - // If the result is different, this was a narrowing conversion. - return ConvertedValue != InitializerValue; - } else { - // Variables are always narrowings. - *isInitializerConstant = false; - return true; - } - } - return false; - } - - default: - // Other kinds of conversions are not narrowings. - return false; - } -} - void InitializationSequence ::AddAddressOverloadResolutionStep(FunctionDecl *Function, @@ -5928,25 +5770,83 @@ void InitializationSequence::dump() const { dump(llvm::errs()); } -static void DiagnoseNarrowingInInitList( - Sema& S, QualType EntityType, const Expr *InitE, - bool Constant, const APValue &ConstantValue) { - if (Constant) { - S.Diag(InitE->getLocStart(), +static void DiagnoseNarrowingInInitList(Sema &S, InitializationSequence &Seq, + QualType EntityType, + const Expr *PreInit, + const Expr *PostInit) { + if (Seq.step_begin() == Seq.step_end() || PreInit->isValueDependent()) + return; + + // A narrowing conversion can only appear as the final implicit conversion in + // an initialization sequence. + const InitializationSequence::Step &LastStep = Seq.step_end()[-1]; + if (LastStep.Kind != InitializationSequence::SK_ConversionSequence) + return; + + const ImplicitConversionSequence &ICS = *LastStep.ICS; + const StandardConversionSequence *SCS = 0; + switch (ICS.getKind()) { + case ImplicitConversionSequence::StandardConversion: + SCS = &ICS.Standard; + break; + case ImplicitConversionSequence::UserDefinedConversion: + SCS = &ICS.UserDefined.After; + break; + case ImplicitConversionSequence::AmbiguousConversion: + case ImplicitConversionSequence::EllipsisConversion: + case ImplicitConversionSequence::BadConversion: + return; + } + + // Determine the type prior to the narrowing conversion. If a conversion + // operator was used, this may be different from both the type of the entity + // and of the pre-initialization expression. + QualType PreNarrowingType = PreInit->getType(); + if (Seq.step_begin() + 1 != Seq.step_end()) + PreNarrowingType = Seq.step_end()[-2].Type; + + // C++11 [dcl.init.list]p7: Check whether this is a narrowing conversion. + APValue ConstantValue; + switch (SCS->isNarrowing(S.Context, PostInit, ConstantValue)) { + case NK_Not_Narrowing: + // No narrowing occurred. + return; + + case NK_Type_Narrowing: + // This was a floating-to-integer conversion, which is always considered a + // narrowing conversion even if the value is a constant and can be + // represented exactly as an integer. + S.Diag(PostInit->getLocStart(), + S.getLangOptions().CPlusPlus0x && !S.getLangOptions().MicrosoftExt + ? diag::err_init_list_type_narrowing + : diag::warn_init_list_type_narrowing) + << PostInit->getSourceRange() + << PreNarrowingType.getLocalUnqualifiedType() + << EntityType.getLocalUnqualifiedType(); + break; + + case NK_Constant_Narrowing: + // A constant value was narrowed. + S.Diag(PostInit->getLocStart(), S.getLangOptions().CPlusPlus0x && !S.getLangOptions().MicrosoftExt ? diag::err_init_list_constant_narrowing : diag::warn_init_list_constant_narrowing) - << InitE->getSourceRange() + << PostInit->getSourceRange() << ConstantValue.getAsString(S.getASTContext(), EntityType) << EntityType.getLocalUnqualifiedType(); - } else - S.Diag(InitE->getLocStart(), + break; + + case NK_Variable_Narrowing: + // A variable's value may have been narrowed. + S.Diag(PostInit->getLocStart(), S.getLangOptions().CPlusPlus0x && !S.getLangOptions().MicrosoftExt ? diag::err_init_list_variable_narrowing : diag::warn_init_list_variable_narrowing) - << InitE->getSourceRange() - << InitE->getType().getLocalUnqualifiedType() + << PostInit->getSourceRange() + << PreNarrowingType.getLocalUnqualifiedType() << EntityType.getLocalUnqualifiedType(); + break; + } llvm::SmallString<128> StaticCast; llvm::raw_svector_ostream OS(StaticCast); @@ -5966,11 +5866,11 @@ static void DiagnoseNarrowingInInitList( return; } OS << ">("; - S.Diag(InitE->getLocStart(), diag::note_init_list_narrowing_override) - << InitE->getSourceRange() - << FixItHint::CreateInsertion(InitE->getLocStart(), OS.str()) + S.Diag(PostInit->getLocStart(), diag::note_init_list_narrowing_override) + << PostInit->getSourceRange() + << FixItHint::CreateInsertion(PostInit->getLocStart(), OS.str()) << FixItHint::CreateInsertion( - S.getPreprocessor().getLocForEndOfToken(InitE->getLocEnd()), ")"); + S.getPreprocessor().getLocForEndOfToken(PostInit->getLocEnd()), ")"); } //===----------------------------------------------------------------------===// @@ -6010,12 +5910,11 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity, InitializationSequence Seq(*this, Entity, Kind, &InitE, 1); Init.release(); - bool Constant = false; - APValue Result; - if (TopLevelOfInitList && - Seq.endsWithNarrowing(Context, InitE, &Constant, &Result)) { - DiagnoseNarrowingInInitList(*this, Entity.getType(), InitE, - Constant, Result); - } - return Seq.Perform(*this, Entity, Kind, MultiExprArg(&InitE, 1)); + ExprResult Result = Seq.Perform(*this, Entity, Kind, MultiExprArg(&InitE, 1)); + + if (!Result.isInvalid() && TopLevelOfInitList) + DiagnoseNarrowingInInitList(*this, Seq, Entity.getType(), + InitE, Result.get()); + + return Result; } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index e39735e256..3708f6c9d0 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -258,6 +258,163 @@ isPointerConversionToVoidPointer(ASTContext& Context) const { return false; } +/// Skip any implicit casts which could be either part of a narrowing conversion +/// or after one in an implicit conversion. +static const Expr *IgnoreNarrowingConversion(const Expr *Converted) { + while (const ImplicitCastExpr *ICE = dyn_cast(Converted)) { + switch (ICE->getCastKind()) { + case CK_NoOp: + case CK_IntegralCast: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + Converted = ICE->getSubExpr(); + continue; + + default: + return Converted; + } + } + + return Converted; +} + +/// Check if this standard conversion sequence represents a narrowing +/// conversion, according to C++11 [dcl.init.list]p7. +/// +/// \param Ctx The AST context. +/// \param Converted The result of applying this standard conversion sequence. +/// \param ConstantValue If this is an NK_Constant_Narrowing conversion, the +/// value of the expression prior to the narrowing conversion. +NarrowingKind +StandardConversionSequence::isNarrowing(ASTContext &Ctx, const Expr *Converted, + APValue &ConstantValue) const { + assert(Ctx.getLangOptions().CPlusPlus && "narrowing check outside C++"); + + // C++11 [dcl.init.list]p7: + // A narrowing conversion is an implicit conversion ... + QualType FromType = getToType(0); + QualType ToType = getToType(1); + switch (Second) { + // -- from a floating-point type to an integer type, or + // + // -- from an integer type or unscoped enumeration type to a floating-point + // type, except where the source is a constant expression and the actual + // value after conversion will fit into the target type and will produce + // the original value when converted back to the original type, or + case ICK_Floating_Integral: + if (FromType->isRealFloatingType() && ToType->isIntegralType(Ctx)) { + return NK_Type_Narrowing; + } else if (FromType->isIntegralType(Ctx) && ToType->isRealFloatingType()) { + llvm::APSInt IntConstantValue; + const Expr *Initializer = IgnoreNarrowingConversion(Converted); + if (Initializer && + Initializer->isIntegerConstantExpr(IntConstantValue, Ctx)) { + // Convert the integer to the floating type. + llvm::APFloat Result(Ctx.getFloatTypeSemantics(ToType)); + Result.convertFromAPInt(IntConstantValue, IntConstantValue.isSigned(), + llvm::APFloat::rmNearestTiesToEven); + // And back. + llvm::APSInt ConvertedValue = IntConstantValue; + bool ignored; + Result.convertToInteger(ConvertedValue, + llvm::APFloat::rmTowardZero, &ignored); + // If the resulting value is different, this was a narrowing conversion. + if (IntConstantValue != ConvertedValue) { + ConstantValue = APValue(IntConstantValue); + return NK_Constant_Narrowing; + } + } else { + // Variables are always narrowings. + return NK_Variable_Narrowing; + } + } + return NK_Not_Narrowing; + + // -- from long double to double or float, or from double to float, except + // where the source is a constant expression and the actual value after + // conversion is within the range of values that can be represented (even + // if it cannot be represented exactly), or + case ICK_Floating_Conversion: + if (FromType->isRealFloatingType() && ToType->isRealFloatingType() && + Ctx.getFloatingTypeOrder(FromType, ToType) == 1) { + // FromType is larger than ToType. + const Expr *Initializer = IgnoreNarrowingConversion(Converted); + if (Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) { + // Constant! + assert(ConstantValue.isFloat()); + llvm::APFloat FloatVal = ConstantValue.getFloat(); + // Convert the source value into the target type. + bool ignored; + llvm::APFloat::opStatus ConvertStatus = FloatVal.convert( + Ctx.getFloatTypeSemantics(ToType), + llvm::APFloat::rmNearestTiesToEven, &ignored); + // If there was no overflow, the source value is within the range of + // values that can be represented. + if (ConvertStatus & llvm::APFloat::opOverflow) + return NK_Constant_Narrowing; + } else { + return NK_Variable_Narrowing; + } + } + return NK_Not_Narrowing; + + // -- from an integer type or unscoped enumeration type to an integer type + // that cannot represent all the values of the original type, except where + // the source is a constant expression and the actual value after + // conversion will fit into the target type and will produce the original + // value when converted back to the original type. + case ICK_Boolean_Conversion: // Bools are integers too. + if (!FromType->isIntegralOrUnscopedEnumerationType()) { + // Boolean conversions can be from pointers and pointers to members + // [conv.bool], and those aren't considered narrowing conversions. + return NK_Not_Narrowing; + } // Otherwise, fall through to the integral case. + case ICK_Integral_Conversion: { + assert(FromType->isIntegralOrUnscopedEnumerationType()); + assert(ToType->isIntegralOrUnscopedEnumerationType()); + const bool FromSigned = FromType->isSignedIntegerOrEnumerationType(); + const unsigned FromWidth = Ctx.getIntWidth(FromType); + const bool ToSigned = ToType->isSignedIntegerOrEnumerationType(); + const unsigned ToWidth = Ctx.getIntWidth(ToType); + + if (FromWidth > ToWidth || + (FromWidth == ToWidth && FromSigned != ToSigned)) { + // Not all values of FromType can be represented in ToType. + llvm::APSInt InitializerValue; + const Expr *Initializer = IgnoreNarrowingConversion(Converted); + if (Initializer->isIntegerConstantExpr(InitializerValue, Ctx)) { + ConstantValue = APValue(InitializerValue); + + // Add a bit to the InitializerValue so we don't have to worry about + // signed vs. unsigned comparisons. + InitializerValue = InitializerValue.extend( + InitializerValue.getBitWidth() + 1); + // Convert the initializer to and from the target width and signed-ness. + llvm::APSInt ConvertedValue = InitializerValue; + ConvertedValue = ConvertedValue.trunc(ToWidth); + ConvertedValue.setIsSigned(ToSigned); + ConvertedValue = ConvertedValue.extend(InitializerValue.getBitWidth()); + ConvertedValue.setIsSigned(InitializerValue.isSigned()); + // If the result is different, this was a narrowing conversion. + if (ConvertedValue != InitializerValue) + return NK_Constant_Narrowing; + } else { + // Variables are always narrowings. + return NK_Variable_Narrowing; + } + } + return NK_Not_Narrowing; + } + + default: + // Other kinds of conversions are not narrowings. + return NK_Not_Narrowing; + } +} + /// DebugPrint - Print this standard conversion sequence to standard /// error. Useful for debugging overloading issues. void StandardConversionSequence::DebugPrint() const { diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp index 2294a4eb08..6b830ba5a5 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p7-0x.cpp @@ -31,12 +31,20 @@ struct Agg { T t; }; +template +struct Convert { + constexpr Convert(T v) : v(v) {} + constexpr operator T() const { return v; } + T v; +}; +template Convert ConvertVar(); + // C++0x [dcl.init.list]p7: A narrowing conversion is an implicit conversion // // * from a floating-point type to an integer type, or void float_to_int() { - Agg a1 = {1.0F}; // expected-error {{ cannot be narrowed }} expected-note {{override}} + Agg a1 = {1.0F}; // expected-error {{type 'float' cannot be narrowed to 'char'}} expected-note {{override}} Agg a2 = {1.0}; // expected-error {{ cannot be narrowed }} expected-note {{override}} Agg a3 = {1.0L}; // expected-error {{ cannot be narrowed }} expected-note {{override}} @@ -46,6 +54,9 @@ void float_to_int() { Agg a4 = {f}; // expected-error {{ cannot be narrowed }} expected-note {{override}} Agg a5 = {d}; // expected-error {{ cannot be narrowed }} expected-note {{override}} Agg a6 = {ld}; // expected-error {{ cannot be narrowed }} expected-note {{override}} + + Agg ce1 = { Convert(1.0) }; // expected-error {{type 'float' cannot be narrowed to 'char'}} expected-note {{override}} + Agg ce2 = { ConvertVar() }; // expected-error {{type 'double' cannot be narrowed to 'char'}} expected-note {{override}} } // * from long double to double or float, or from double to float, except where @@ -61,7 +72,7 @@ void shrink_float() { // Variables. Agg f1 = {f}; // OK (no-op) - Agg f2 = {d}; // expected-error {{ cannot be narrowed }} expected-note {{override}} + Agg f2 = {d}; // expected-error {{non-constant-expression cannot be narrowed from type 'double' to 'float'}} expected-note {{override}} Agg f3 = {ld}; // expected-error {{ cannot be narrowed }} expected-note {{override}} // Exact constants. Agg f4 = {1.0}; // OK (double constant represented exactly) @@ -70,7 +81,7 @@ void shrink_float() { Agg f6 = {0.1}; // OK (double constant in range but rounded) Agg f7 = {0.1L}; // OK (long double constant in range but rounded) // Out of range constants. - Agg f8 = {1E50}; // expected-error {{ cannot be narrowed }} expected-note {{override}} + Agg f8 = {1E50}; // expected-error {{constant expression evaluates to 1.000000e+50 which cannot be narrowed to type 'float'}} expected-note {{override}} Agg f9 = {1E50L}; // expected-error {{ cannot be narrowed }} expected-note {{override}} // More complex constant expression. constexpr long double e40 = 1E40L, e30 = 1E30L, e39 = 1E39L; @@ -89,6 +100,9 @@ void shrink_float() { // More complex constant expression. constexpr long double e315 = 1E315L, e305 = 1E305L, e314 = 1E314L; Agg d7 = {e315 - 5 * e314 + e305 - 5 * e314}; // OK + + Agg ce1 = { Convert(1e300) }; // expected-error {{constant expression evaluates to 1.000000e+300 which cannot be narrowed to type 'float'}} expected-note {{override}} + Agg ce2 = { ConvertVar() }; // expected-error {{non-constant-expression cannot be narrowed from type 'long double' to 'double'}} expected-note {{override}} } // * from an integer type or unscoped enumeration type to a floating-point type, @@ -107,6 +121,9 @@ void int_to_float() { // Constants. Agg f4 = {12345678}; // OK (exactly fits in a float) Agg f5 = {123456789}; // expected-error {{ cannot be narrowed }} expected-note {{override}} + + Agg ce1 = { Convert(123456789) }; // expected-error {{constant expression evaluates to 123456789 which cannot be narrowed to type 'float'}} expected-note {{override}} + Agg ce2 = { ConvertVar() }; // expected-error {{non-constant-expression cannot be narrowed from type 'long long' to 'double'}} expected-note {{override}} } // * from an integer type or unscoped enumeration type to an integer type that @@ -147,6 +164,9 @@ void shrink_int() { // Conversions from pointers to booleans aren't narrowing conversions. Agg b = {&b1}; // OK + + Agg ce1 = { Convert(100000) }; // expected-error {{constant expression evaluates to 100000 which cannot be narrowed to type 'short'}} expected-note {{override}} expected-warning {{changes value from 100000 to -31072}} + Agg ce2 = { ConvertVar() }; // expected-error {{non-constant-expression cannot be narrowed from type 'short' to 'char'}} expected-note {{override}} } // Be sure that type- and value-dependent expressions in templates get the error