From 09f41cf63f4df0bf4e98ee473e44e9a95b68f0ff Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 14 Jan 2009 15:45:31 +0000 Subject: [PATCH] Introduce support for C++0x explicit conversion operators (N2437) Small cleanup in the handling of user-defined conversions. Also, implement an optimization when constructing a call. We avoid recomputing implicit conversion sequences and instead use those conversion sequences that we computed as part of overload resolution. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62231 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 6 +- lib/Sema/Sema.h | 29 +++++--- lib/Sema/SemaDecl.cpp | 36 +++++++--- lib/Sema/SemaDeclCXX.cpp | 19 +++-- lib/Sema/SemaExpr.cpp | 14 ++-- lib/Sema/SemaExprCXX.cpp | 38 ++++++---- lib/Sema/SemaInit.cpp | 2 +- lib/Sema/SemaOverload.cpp | 94 +++++++++++++++++++------ test/SemaCXX/condition.cpp | 12 ++-- www/cxx_status.html | 11 +++ 10 files changed, 188 insertions(+), 73 deletions(-) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 10117b98ea..8ad14b1e47 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1349,7 +1349,7 @@ DIAG(err_invalid_use_of_array_type, ERROR, DIAG(err_type_defined_in_condition, ERROR, "types may not be defined in conditions") DIAG(err_typecheck_bool_condition, ERROR, - "expression must have bool type (or be convertible to bool) (%0 invalid)") + "value of type %0 is not contextually convertible to 'bool'") DIAG(err_expected_class_or_namespace, ERROR, "expected a class or namespace") DIAG(err_invalid_declarator_scope, ERROR, @@ -1582,6 +1582,10 @@ DIAG(warn_conv_to_void_not_used, WARNING, DIAG(warn_not_compound_assign, WARNING, "use of unary operator that may be intended as compound assignment (%0=)") +// C++0x explicit conversion operators +DIAG(warn_explicit_conversion_functions, WARNING, + "explicit conversion functions are a C++0x extension") + // CHECK: printf format string errors DIAG(warn_printf_not_string_constant, WARNING, "format string is not a string literal (potentially insecure)") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index dde9b761e2..3a5f93fc8f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -281,7 +281,8 @@ public: virtual void ActOnParamUnparsedDefaultArgument(DeclTy *param, SourceLocation EqualLoc); virtual void ActOnParamDefaultArgumentError(DeclTy *param); - void AddInitializerToDecl(DeclTy *dcl, ExprArg init); + virtual void AddInitializerToDecl(DeclTy *dcl, ExprArg init); + void AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit); void ActOnUninitializedDecl(DeclTy *dcl); virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group); @@ -390,7 +391,8 @@ public: OverloadedFunctionDecl::function_iterator &MatchedDecl); ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType, - bool SuppressUserConversions = false); + bool SuppressUserConversions = false, + bool AllowExplicit = false); bool IsStandardConversion(Expr *From, QualType ToType, StandardConversionSequence& SCS); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); @@ -402,7 +404,8 @@ public: bool CheckPointerConversion(Expr *From, QualType ToType); bool IsQualificationConversion(QualType FromType, QualType ToType); bool IsUserDefinedConversion(Expr *From, QualType ToType, - UserDefinedConversionSequence& User); + UserDefinedConversionSequence& User, + bool AllowExplicit = false); ImplicitConversionSequence::CompareKind CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1, @@ -430,6 +433,9 @@ public: TryObjectArgumentInitialization(Expr *From, CXXMethodDecl *Method); bool PerformObjectArgumentInitialization(Expr *&From, CXXMethodDecl *Method); + ImplicitConversionSequence TryContextuallyConvertToBool(Expr *From); + bool PerformContextuallyConvertToBool(Expr *&From); + /// OverloadingResult - Capture the result of performing overload /// resolution. enum OverloadingResult { @@ -459,7 +465,8 @@ public: void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool IsAssignmentOperator = false); + bool IsAssignmentOperator = false, + unsigned NumContextualBoolArguments = 0); void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet); @@ -497,7 +504,7 @@ public: ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, SourceLocation MemberLoc, IdentifierInfo &Member); - + /// Helpers for dealing with function parameters. bool CheckParmsForFunctionDef(FunctionDecl *FD); void CheckCXXDefaultArguments(FunctionDecl *FD); @@ -1360,6 +1367,9 @@ public: bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); bool PerformImplicitConversion(Expr *&From, QualType ToType, + const char *Flavor, bool AllowExplicit = false); + bool PerformImplicitConversion(Expr *&From, QualType ToType, + const ImplicitConversionSequence& ICS, const char *Flavor); bool PerformImplicitConversion(Expr *&From, QualType ToType, const StandardConversionSequence& SCS, @@ -1416,8 +1426,10 @@ public: /// type checking declaration initializers (C99 6.7.8) friend class InitListChecker; bool CheckInitializerTypes(Expr *&simpleInit_or_initList, QualType &declType, - SourceLocation InitLoc,DeclarationName InitEntity); - bool CheckSingleInitializer(Expr *&simpleInit, QualType declType); + SourceLocation InitLoc,DeclarationName InitEntity, + bool DirectInit); + bool CheckSingleInitializer(Expr *&simpleInit, QualType declType, + bool DirectInit); bool CheckForConstantInitializer(Expr *e, QualType t); bool CheckArithmeticConstantExpression(const Expr* e); bool CheckAddressConstantExpression(const Expr* e); @@ -1455,7 +1467,8 @@ public: bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType, ImplicitConversionSequence *ICS = 0, - bool SuppressUserConversions = false); + bool SuppressUserConversions = false, + bool AllowExplicit = false); /// CheckCastTypes - Check type constraints for casting between types. bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c124c469ad..8ba6f31da8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1120,13 +1120,22 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, return Anon; } -bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) { +bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType, + bool DirectInit) { // Get the type before calling CheckSingleAssignmentConstraints(), since // it can promote the expression. QualType InitType = Init->getType(); - if (getLangOptions().CPlusPlus) - return PerformCopyInitialization(Init, DeclType, "initializing"); + if (getLangOptions().CPlusPlus) { + // FIXME: I dislike this error message. A lot. + if (PerformImplicitConversion(Init, DeclType, "initializing", DirectInit)) + return Diag(Init->getSourceRange().getBegin(), + diag::err_typecheck_convert_incompatible) + << DeclType << Init->getType() << "initializing" + << Init->getSourceRange(); + + return false; + } AssignConvertType ConvTy = CheckSingleAssignmentConstraints(DeclType, Init); return DiagnoseAssignmentResult(ConvTy, Init->getLocStart(), DeclType, @@ -1169,7 +1178,8 @@ StringLiteral *Sema::IsStringLiteralInit(Expr *Init, QualType DeclType) { bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, SourceLocation InitLoc, - DeclarationName InitEntity) { + DeclarationName InitEntity, + bool DirectInit) { if (DeclType->isDependentType() || Init->isTypeDependent()) return false; @@ -1178,7 +1188,7 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, // (8.3.2), shall be initialized by an object, or function, of // type T or by an object that can be converted into a T. if (DeclType->isReferenceType()) - return CheckReferenceInit(Init, DeclType); + return CheckReferenceInit(Init, DeclType, 0, false, DirectInit); // C99 6.7.8p3: The type of the entity to be initialized shall be an array // of unknown size ("[]") or an object type that is not a variable array type. @@ -1208,7 +1218,8 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, CXXConstructorDecl *Constructor = PerformInitializationByConstructor(DeclType, &Init, 1, InitLoc, Init->getSourceRange(), - InitEntity, IK_Copy); + InitEntity, + DirectInit? IK_Direct : IK_Copy); return Constructor == 0; } @@ -1244,7 +1255,7 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, return Diag(Init->getLocStart(), diag::err_array_init_list_required) << Init->getSourceRange(); - return CheckSingleInitializer(Init, DeclType); + return CheckSingleInitializer(Init, DeclType, DirectInit); } else if (getLangOptions().CPlusPlus) { // C++ [dcl.init]p14: // [...] If the class is an aggregate (8.5.1), and the initializer @@ -2366,6 +2377,13 @@ bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) { } void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init) { + AddInitializerToDecl(dcl, move(init), /*DirectInit=*/false); +} + +/// AddInitializerToDecl - Adds the initializer Init to the +/// declaration dcl. If DirectInit is true, this is C++ direct +/// initialization rather than copy initialization. +void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init, bool DirectInit) { Decl *RealDecl = static_cast(dcl); Expr *Init = static_cast(init.release()); assert(Init && "missing initializer"); @@ -2394,7 +2412,7 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init) { VDecl->setInvalidDecl(); } else if (!VDecl->isInvalidDecl()) { if (CheckInitializerTypes(Init, DclT, VDecl->getLocation(), - VDecl->getDeclName())) + VDecl->getDeclName(), DirectInit)) VDecl->setInvalidDecl(); // C++ 3.6.2p2, allow dynamic initialization of static initializers. @@ -2408,7 +2426,7 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprArg init) { Diag(VDecl->getLocation(), diag::warn_extern_init); if (!VDecl->isInvalidDecl()) if (CheckInitializerTypes(Init, DclT, VDecl->getLocation(), - VDecl->getDeclName())) + VDecl->getDeclName(), DirectInit)) VDecl->setInvalidDecl(); // C++ 3.6.2p2, allow dynamic initialization of static initializers. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 65c1416124..20f05228ac 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -128,7 +128,8 @@ Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc, Expr *DefaultArgPtr = DefaultArg.get(); bool DefaultInitFailed = CheckInitializerTypes(DefaultArgPtr, ParamType, EqualLoc, - Param->getDeclName()); + Param->getDeclName(), + /*DirectInit=*/false); if (DefaultArgPtr != DefaultArg.get()) { DefaultArg.take(); DefaultArg.reset(DefaultArgPtr); @@ -1299,6 +1300,12 @@ bool Sema::CheckConversionDeclarator(Declarator &D, QualType &R, R = Context.getFunctionType(ConvType, 0, 0, false, R->getAsFunctionTypeProto()->getTypeQuals()); + // C++0x explicit conversion operators. + if (D.getDeclSpec().isExplicitSpecified() && !getLangOptions().CPlusPlus0x) + Diag(D.getDeclSpec().getExplicitSpecLoc(), + diag::warn_explicit_conversion_functions) + << SourceRange(D.getDeclSpec().getExplicitSpecLoc()); + return isInvalid; } @@ -1538,7 +1545,7 @@ void Sema::AddCXXDirectInitializerToDecl(DeclTy *Dcl, SourceLocation LParenLoc, assert(NumExprs == 1 && "Expected 1 expression"); // Set the init expression, handles conversions. - AddInitializerToDecl(Dcl, ExprArg(*this, ExprTys[0])); + AddInitializerToDecl(Dcl, ExprArg(*this, ExprTys[0]), /*DirectInit=*/true); } /// PerformInitializationByConstructor - Perform initialization by @@ -1677,10 +1684,13 @@ Sema::CompareReferenceRelationship(QualType T1, QualType T2, /// /// When @p SuppressUserConversions, user-defined conversions are /// suppressed. +/// When @p AllowExplicit, we also permit explicit user-defined +/// conversion functions. bool Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, ImplicitConversionSequence *ICS, - bool SuppressUserConversions) { + bool SuppressUserConversions, + bool AllowExplicit) { assert(DeclType->isReferenceType() && "Reference init needs a reference"); QualType T1 = DeclType->getAsReferenceType()->getPointeeType(); @@ -1780,7 +1790,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // If the conversion function doesn't return a reference type, // it can't be considered for this conversion. // FIXME: This will change when we support rvalue references. - if (Conv->getConversionType()->isReferenceType()) + if (Conv->getConversionType()->isReferenceType() && + (AllowExplicit || !Conv->isExplicit())) AddConversionCandidate(Conv, Init, DeclType, CandidateSet); } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0c97611618..f802f5126c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1873,7 +1873,7 @@ ActOnCompoundLiteral(SourceLocation LParenLoc, TypeTy *Ty, } if (CheckInitializerTypes(literalExpr, literalType, LParenLoc, - DeclarationName())) + DeclarationName(), /*FIXME:DirectInit=*/false)) return true; bool isFileScope = getCurFunctionOrMethodDecl() == 0; @@ -3546,10 +3546,10 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, // We matched a built-in operator. Convert the arguments, then // break out so that we will build the appropriate built-in // operator node. - if (PerformCopyInitialization(lhs, Best->BuiltinTypes.ParamTypes[0], - "passing") || - PerformCopyInitialization(rhs, Best->BuiltinTypes.ParamTypes[1], - "passing")) + if (PerformImplicitConversion(lhs, Best->BuiltinTypes.ParamTypes[0], + Best->Conversions[0], "passing") || + PerformImplicitConversion(rhs, Best->BuiltinTypes.ParamTypes[1], + Best->Conversions[1], "passing")) return true; break; @@ -3644,8 +3644,8 @@ Action::ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, // We matched a built-in operator. Convert the arguments, then // break out so that we will build the appropriate built-in // operator node. - if (PerformCopyInitialization(Input, Best->BuiltinTypes.ParamTypes[0], - "passing")) + if (PerformImplicitConversion(Input, Best->BuiltinTypes.ParamTypes[0], + Best->Conversions[0], "passing")) return true; break; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index efc664cf53..51cdc5c378 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -291,7 +291,8 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, // Object is direct-initialized. // FIXME: WHAT DeclarationName do we pass in here? if (CheckInitializerTypes(ConsArgs[0], AllocType, StartLoc, - DeclarationName() /*AllocType.getAsString()*/)) + DeclarationName() /*AllocType.getAsString()*/, + /*DirectInit=*/true)) return true; } else { Diag(StartLoc, diag::err_builtin_direct_init_more_than_one_arg) @@ -650,13 +651,7 @@ bool Sema::CheckCXXBooleanCondition(Expr *&CondExpr) { // The value of a condition that is an expression is the value of the // expression, implicitly converted to bool. // - QualType Ty = CondExpr->getType(); // Save the type. - AssignConvertType - ConvTy = CheckSingleAssignmentConstraints(Context.BoolTy, CondExpr); - if (ConvTy == Incompatible) - return Diag(CondExpr->getLocStart(), diag::err_typecheck_bool_condition) - << Ty << CondExpr->getSourceRange(); - return false; + return PerformContextuallyConvertToBool(CondExpr); } /// Helper function to determine whether this is the (deprecated) C++ @@ -694,12 +689,27 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) { /// expression From to the type ToType. Returns true if there was an /// error, false otherwise. The expression From is replaced with the /// converted expression. Flavor is the kind of conversion we're -/// performing, used in the error message. +/// performing, used in the error message. If @p AllowExplicit, +/// explicit user-defined conversions are permitted. bool Sema::PerformImplicitConversion(Expr *&From, QualType ToType, - const char *Flavor) + const char *Flavor, bool AllowExplicit) { - ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType); + ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType, false, + AllowExplicit); + return PerformImplicitConversion(From, ToType, ICS, Flavor); +} + +/// PerformImplicitConversion - Perform an implicit conversion of the +/// expression From to the type ToType using the pre-computed implicit +/// conversion sequence ICS. Returns true if there was an error, false +/// otherwise. The expression From is replaced with the converted +/// expression. Flavor is the kind of conversion we're performing, +/// used in the error message. +bool +Sema::PerformImplicitConversion(Expr *&From, QualType ToType, + const ImplicitConversionSequence &ICS, + const char* Flavor) { switch (ICS.ConversionKind) { case ImplicitConversionSequence::StandardConversion: if (PerformImplicitConversion(From, ToType, ICS.Standard, Flavor)) @@ -710,7 +720,8 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, // FIXME: This is, of course, wrong. We'll need to actually call // the constructor or conversion operator, and then cope with the // standard conversions. - ImpCastExprToType(From, ToType); + ImpCastExprToType(From, ToType.getNonReferenceType(), + ToType->isReferenceType()); return false; case ImplicitConversionSequence::EllipsisConversion: @@ -734,8 +745,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, bool Sema::PerformImplicitConversion(Expr *&From, QualType ToType, const StandardConversionSequence& SCS, - const char *Flavor) -{ + const char *Flavor) { // Overall FIXME: we are recomputing too many types here and doing // far too much extra work. What this means is that we need to keep // track of more information that is computed when we try the diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 031fe27ddf..f9c91ecaab 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -193,7 +193,7 @@ void InitListChecker::CheckScalarType(InitListExpr *IList, QualType &DeclType, return; } Expr *savExpr = expr; // Might be promoted by CheckSingleInitializer. - if (SemaRef->CheckSingleInitializer(expr, DeclType)) + if (SemaRef->CheckSingleInitializer(expr, DeclType, false)) hadError = true; // types weren't compatible. else if (savExpr != expr) // The type was promoted, update initializer list. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index d74a5d54b9..fca1556028 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -361,15 +361,18 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, /// /// If @p SuppressUserConversions, then user-defined conversions are /// not permitted. +/// If @p AllowExplicit, then explicit user-defined conversions are +/// permitted. ImplicitConversionSequence Sema::TryImplicitConversion(Expr* From, QualType ToType, - bool SuppressUserConversions) + bool SuppressUserConversions, + bool AllowExplict) { ImplicitConversionSequence ICS; if (IsStandardConversion(From, ToType, ICS.Standard)) ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; else if (!SuppressUserConversions && - IsUserDefinedConversion(From, ToType, ICS.UserDefined)) { + IsUserDefinedConversion(From, ToType, ICS.UserDefined, AllowExplict)) { ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion; // C++ [over.ics.user]p4: // A conversion of an expression of class type to the same class @@ -1097,9 +1100,12 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) /// exists, User will contain the user-defined conversion sequence /// that performs such a conversion and this routine will return /// true. Otherwise, this routine returns false and User is -/// unspecified. +/// unspecified. AllowExplicit is true if the conversion should +/// consider C++0x "explicit" conversion functions as well as +/// non-explicit conversion functions (C++0x [class.conv.fct]p2). bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, - UserDefinedConversionSequence& User) + UserDefinedConversionSequence& User, + bool AllowExplicit) { OverloadCandidateSet CandidateSet; if (const CXXRecordType *ToRecordType @@ -1137,7 +1143,8 @@ bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, = Conversions->function_begin(); Func != Conversions->function_end(); ++Func) { CXXConversionDecl *Conv = cast(*Func); - AddConversionCandidate(Conv, From, ToType, CandidateSet); + if (AllowExplicit || !Conv->isExplicit()) + AddConversionCandidate(Conv, From, ToType, CandidateSet); } } @@ -1748,6 +1755,24 @@ Sema::PerformObjectArgumentInitialization(Expr *&From, CXXMethodDecl *Method) { return false; } +/// TryContextuallyConvertToBool - Attempt to contextually convert the +/// expression From to bool (C++0x [conv]p3). +ImplicitConversionSequence Sema::TryContextuallyConvertToBool(Expr *From) { + return TryImplicitConversion(From, Context.BoolTy, false, true); +} + +/// PerformContextuallyConvertToBool - Perform a contextual conversion +/// of the expression From to bool (C++0x [conv]p3). +bool Sema::PerformContextuallyConvertToBool(Expr *&From) { + ImplicitConversionSequence ICS = TryContextuallyConvertToBool(From); + if (!PerformImplicitConversion(From, Context.BoolTy, ICS, "converting")) + return false; + + return Diag(From->getSourceRange().getBegin(), + diag::err_typecheck_bool_condition) + << From->getType() << From->getSourceRange(); +} + /// AddOverloadCandidate - Adds the given function to the set of /// candidate functions, using the given function call arguments. If /// @p SuppressUserConversions, then don't allow user-defined @@ -2201,11 +2226,14 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S, /// of the built-in candidate, respectively. Args and NumArgs are the /// arguments being passed to the candidate. IsAssignmentOperator /// should be true when this built-in candidate is an assignment -/// operator. +/// operator. NumContextualBoolArguments is the number of arguments +/// (at the beginning of the argument list) that will be contextually +/// converted to bool. void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool IsAssignmentOperator) { + bool IsAssignmentOperator, + unsigned NumContextualBoolArguments) { // Add this candidate CandidateSet.push_back(OverloadCandidate()); OverloadCandidate& Candidate = CandidateSet.back(); @@ -2233,9 +2261,15 @@ void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, // conversions, since that is the only way that initialization of // a reference to a non-class type can occur from something that // is not of the same type. - Candidate.Conversions[ArgIdx] - = TryCopyInitialization(Args[ArgIdx], ParamTys[ArgIdx], - ArgIdx == 0 && IsAssignmentOperator); + if (ArgIdx < NumContextualBoolArguments) { + assert(ParamTys[ArgIdx] == Context.BoolTy && + "Contextual conversion to bool requires bool type"); + Candidate.Conversions[ArgIdx] = TryContextuallyConvertToBool(Args[ArgIdx]); + } else { + Candidate.Conversions[ArgIdx] + = TryCopyInitialization(Args[ArgIdx], ParamTys[ArgIdx], + ArgIdx == 0 && IsAssignmentOperator); + } if (Candidate.Conversions[ArgIdx].ConversionKind == ImplicitConversionSequence::BadConversion) { Candidate.Viable = false; @@ -2309,7 +2343,8 @@ public: BuiltinCandidateTypeSet(ASTContext &Context) : Context(Context) { } - void AddTypesConvertedFrom(QualType Ty, bool AllowUserConversions = true); + void AddTypesConvertedFrom(QualType Ty, bool AllowUserConversions, + bool AllowExplicitConversions); /// pointer_begin - First pointer type found; iterator pointer_begin() { return PointerTypes.begin(); } @@ -2358,9 +2393,15 @@ bool BuiltinCandidateTypeSet::AddWithMoreQualifiedTypeVariants(QualType Ty) { /// AddTypesConvertedFrom - Add each of the types to which the type @p /// Ty can be implicit converted to the given set of @p Types. We're -/// primarily interested in pointer types, enumeration types, -void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, - bool AllowUserConversions) { +/// primarily interested in pointer types and enumeration types. +/// AllowUserConversions is true if we should look at the conversion +/// functions of a class type, and AllowExplicitConversions if we +/// should also include the explicit conversion functions of a class +/// type. +void +BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, + bool AllowUserConversions, + bool AllowExplicitConversions) { // Only deal with canonical types. Ty = Context.getCanonicalType(Ty); @@ -2399,7 +2440,7 @@ void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, // Add the pointer type, recursively, so that we get all of // the indirect base classes, too. - AddTypesConvertedFrom(Context.getPointerType(BaseTy), false); + AddTypesConvertedFrom(Context.getPointerType(BaseTy), false, false); } } } else if (Ty->isEnumeralType()) { @@ -2414,7 +2455,8 @@ void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, = Conversions->function_begin(); Func != Conversions->function_end(); ++Func) { CXXConversionDecl *Conv = cast(*Func); - AddTypesConvertedFrom(Conv->getConversionType(), false); + if (AllowExplicitConversions || !Conv->isExplicit()) + AddTypesConvertedFrom(Conv->getConversionType(), false, false); } } } @@ -2461,7 +2503,11 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, Op == OO_ArrowStar || Op == OO_PlusPlus || Op == OO_MinusMinus || (Op == OO_Star && NumArgs == 1)) { for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) - CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType()); + CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType(), + true, + (Op == OO_Exclaim || + Op == OO_AmpAmp || + Op == OO_PipePipe)); } bool isComparison = false; @@ -2811,14 +2857,14 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, ParamTypes[0] = Context.getReferenceType(*Enum); ParamTypes[1] = *Enum; AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, - /*IsAssignmentOperator=*/true); + /*IsAssignmentOperator=*/false); if (!Context.getCanonicalType(*Enum).isVolatileQualified()) { // volatile T& operator=(volatile T&, T) ParamTypes[0] = Context.getReferenceType((*Enum).withVolatile()); ParamTypes[1] = *Enum; AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, - /*IsAssignmentOperator=*/true); + /*IsAssignmentOperator=*/false); } } // Fall through. @@ -2919,8 +2965,6 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, ParamTypes[1] = ArithmeticTypes[Right]; // Add this built-in operator as a candidate (VQ is empty). - // FIXME: We should be caching these declarations somewhere, - // rather than re-building them every time. ParamTypes[0] = Context.getReferenceType(ArithmeticTypes[Left]); AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); @@ -2942,7 +2986,9 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, // bool operator&&(bool, bool); [BELOW] // bool operator||(bool, bool); [BELOW] QualType ParamTy = Context.BoolTy; - AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet); + AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet, + /*IsAssignmentOperator=*/false, + /*NumContextualBoolArguments=*/1); break; } @@ -2956,7 +3002,9 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, // bool operator&&(bool, bool); // bool operator||(bool, bool); QualType ParamTypes[2] = { Context.BoolTy, Context.BoolTy }; - AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet); + AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet, + /*IsAssignmentOperator=*/false, + /*NumContextualBoolArguments=*/2); break; } diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp index 43117e61c3..eb1e095c9c 100644 --- a/test/SemaCXX/condition.cpp +++ b/test/SemaCXX/condition.cpp @@ -10,14 +10,14 @@ void test() { while (int f()=0) ; // expected-error {{a function type is not allowed here}} struct S {} s; - if (s) ++x; // expected-error {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} - while (struct S x=s) ; // expected-error {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} - do ; while (s); // expected-error {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} - for (;s;) ; // expected-error {{expression must have bool type (or be convertible to bool) ('struct S' invalid)}} + if (s) ++x; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} + while (struct S x=s) ; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} + do ; while (s); // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} + for (;s;) ; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} switch (s) {} // expected-error {{statement requires expression of integer type ('struct S' invalid)}} - while (struct S {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{expression must have bool type}} - while (struct {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{expression must have bool type}} + while (struct S {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} + while (struct {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize 'x' with an rvalue of type 'int'}} expected-error {{value of type 'struct ' is not contextually convertible to 'bool'}} switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{incompatible type}} if (int x=0) { // expected-note {{previous definition is here}} diff --git a/www/cxx_status.html b/www/cxx_status.html index 9a9f12604b..fed62884ac 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -1541,6 +1541,17 @@ welcome!

  D.4 [depr.string]   D.5 [depr.c.headers] E [extendid] + + C++0x Features + + Explicit conversion operators (N2437) + ✓ + + + + No name mangling; ASTs don't contain calls to conversion operators + + -- 2.40.0