]> granicus.if.org Git - clang/commitdiff
Implement C++11 [over.match.copy]p1b2, which allows the use of
authorDouglas Gregor <dgregor@apple.com>
Fri, 24 Feb 2012 23:56:31 +0000 (23:56 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 24 Feb 2012 23:56:31 +0000 (23:56 +0000)
explicit conversion functions to initialize the argument to a
copy/move constructor that itself is the subject of direct
initialization. Since we don't have that much context in overload
resolution, we end up threading more flags :(.

Fixes <rdar://problem/10903741> / PR10456.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151409 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Initialization.h
include/clang/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaInit.cpp
lib/Sema/SemaOverload.cpp
test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp [new file with mode: 0644]
test/SemaCXX/explicit.cpp

index a36b5e6bd80ce869c5d7ebc5c4617ba42f99cc53..1c31c574b1a9740c2346edffd0ab283d777524d8 100644 (file)
@@ -365,17 +365,18 @@ class InitializationKind {
 public:
   /// \brief The kind of initialization being performed.
   enum InitKind {
-    IK_Direct,     ///< Direct initialization
-    IK_DirectList, ///< Direct list-initialization
-    IK_Copy,       ///< Copy initialization
-    IK_Default,    ///< Default initialization
-    IK_Value       ///< Value initialization
+    IK_Direct,       ///< Direct initialization
+    IK_DirectList,   ///< Direct list-initialization
+    IK_Copy,         ///< Copy initialization
+    IK_Default,      ///< Default initialization
+    IK_Value         ///< Value initialization
   };
   
 private:
   /// \brief The context of the initialization.
   enum InitContext {
     IC_Normal,         ///< Normal context
+    IC_ExplicitConvs,  ///< Normal context, but allows explicit conversion funcs
     IC_Implicit,       ///< Implicit context (value initialization)
     IC_StaticCast,     ///< Static cast context
     IC_CStyleCast,     ///< C-style cast context
@@ -442,8 +443,11 @@ public:
 
   /// \brief Create a copy initialization.
   static InitializationKind CreateCopy(SourceLocation InitLoc,
-                                       SourceLocation EqualLoc) {
-    return InitializationKind(IK_Copy, IC_Normal, InitLoc, EqualLoc, EqualLoc);
+                                       SourceLocation EqualLoc,
+                                       bool AllowExplicitConvs = false) {
+    return InitializationKind(IK_Copy, 
+                              AllowExplicitConvs? IC_ExplicitConvs : IC_Normal,
+                              InitLoc, EqualLoc, EqualLoc);
   }
   
   /// \brief Create a default initialization.
@@ -511,6 +515,12 @@ public:
   ///        constructors.
   bool AllowExplicit() const { return !isCopyInit(); }
 
+  /// \brief Retrieve whether this initialization allows the use of explicit
+  /// conversion functions.
+  bool allowExplicitConversionFunctions() const {
+    return !isCopyInit() || Context == IC_ExplicitConvs;
+  }
+  
   /// \brief Retrieve the source range containing the locations of the open
   /// and closing parentheses for value and direct initializations.
   SourceRange getParenRange() const {
index 8131c6372ee4afab66942552dfc912486f19e05a..875b021f96971891829c1bd450cfa78ab5941fd8 100644 (file)
@@ -1458,7 +1458,8 @@ public:
   ExprResult PerformCopyInitialization(const InitializedEntity &Entity,
                                        SourceLocation EqualLoc,
                                        ExprResult Init,
-                                       bool TopLevelOfInitList = false);
+                                       bool TopLevelOfInitList = false,
+                                       bool AllowExplicit = false);
   ExprResult PerformObjectArgumentInitialization(Expr *From,
                                                  NestedNameSpecifier *Qualifier,
                                                  NamedDecl *FoundDecl,
@@ -1502,7 +1503,8 @@ public:
                             Expr **Args, unsigned NumArgs,
                             OverloadCandidateSet& CandidateSet,
                             bool SuppressUserConversions = false,
-                            bool PartialOverloading = false);
+                            bool PartialOverloading = false,
+                            bool AllowExplicit = false);
   void AddFunctionCandidates(const UnresolvedSetImpl &Functions,
                              Expr **Args, unsigned NumArgs,
                              OverloadCandidateSet& CandidateSet,
@@ -3128,7 +3130,8 @@ public:
   bool CompleteConstructorCall(CXXConstructorDecl *Constructor,
                                MultiExprArg ArgsPtr,
                                SourceLocation Loc,
-                               ASTOwningVector<Expr*> &ConvertedArgs);
+                               ASTOwningVector<Expr*> &ConvertedArgs,
+                               bool AllowExplicit = false);
 
   ParsedType getDestructorName(SourceLocation TildeLoc,
                                IdentifierInfo &II, SourceLocation NameLoc,
@@ -5911,7 +5914,8 @@ public:
                               unsigned FirstProtoArg,
                               Expr **Args, unsigned NumArgs,
                               SmallVector<Expr *, 8> &AllArgs,
-                              VariadicCallType CallType = VariadicDoesNotApply);
+                              VariadicCallType CallType = VariadicDoesNotApply,
+                              bool AllowExplicit = false);
 
   // DefaultVariadicArgumentPromotion - Like DefaultArgumentPromotion, but
   // will warn if the resulting type is not a POD type.
index 6d2f3b252b1e918bd5a9ca6abfa810939a5caaac..a2180be53c4b06e4c57a25987197bc82bfd3709a 100644 (file)
@@ -9032,7 +9032,8 @@ bool
 Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
                               MultiExprArg ArgsPtr,
                               SourceLocation Loc,                                    
-                              ASTOwningVector<Expr*> &ConvertedArgs) {
+                              ASTOwningVector<Expr*> &ConvertedArgs,
+                              bool AllowExplicit) {
   // FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall.
   unsigned NumArgs = ArgsPtr.size();
   Expr **Args = (Expr **)ArgsPtr.get();
@@ -9053,7 +9054,7 @@ Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
   SmallVector<Expr *, 8> AllArgs;
   bool Invalid = GatherArgumentsForCall(Loc, Constructor,
                                         Proto, 0, Args, NumArgs, AllArgs, 
-                                        CallType);
+                                        CallType, AllowExplicit);
   ConvertedArgs.append(AllArgs.begin(), AllArgs.end());
 
   DiagnoseSentinelCalls(Constructor, Loc, AllArgs.data(), AllArgs.size());
index fcb2d410cb20dc04f68b3db1fa504a6fb4c30597..2868b822e6d939969f307c06ac5d4405082eae8c 100644 (file)
@@ -3248,7 +3248,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc,
                                   unsigned FirstProtoArg,
                                   Expr **Args, unsigned NumArgs,
                                   SmallVector<Expr *, 8> &AllArgs,
-                                  VariadicCallType CallType) {
+                                  VariadicCallType CallType,
+                                  bool AllowExplicit) {
   unsigned NumArgsInProto = Proto->getNumArgs();
   unsigned NumArgsToCheck = NumArgs;
   bool Invalid = false;
@@ -3288,7 +3289,9 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc,
                                                       Proto->isArgConsumed(i));
       ExprResult ArgE = PerformCopyInitialization(Entity,
                                                   SourceLocation(),
-                                                  Owned(Arg));
+                                                  Owned(Arg),
+                                                  /*TopLevelOfInitList=*/false,
+                                                  AllowExplicit);
       if (ArgE.isInvalid())
         return true;
 
index fcc74e7d3fb0cb5aba88848e167db38709aa587b..df0a49b79e8f0baa2f3c78714d3826c2cec57b08 100644 (file)
@@ -2827,10 +2827,22 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc,
                                        /*ExplicitArgs*/ 0,
                                        Args, NumArgs, CandidateSet,
                                        SuppressUserConversions);
-      else
+      else {
+        // C++ [over.match.copy]p1:
+        //   - When initializing a temporary to be bound to the first parameter 
+        //     of a constructor that takes a reference to possibly cv-qualified 
+        //     T as its first argument, called with a single argument in the 
+        //     context of direct-initialization, explicit conversion functions
+        //     are also considered.
+        bool AllowExplicitConv = AllowExplicit && !CopyInitializing && 
+                                 NumArgs == 1 &&
+                                 Constructor->isCopyOrMoveConstructor();
         S.AddOverloadCandidate(Constructor, FoundDecl,
                                Args, NumArgs, CandidateSet,
-                               SuppressUserConversions);
+                               SuppressUserConversions,
+                               /*PartialOverloading=*/false,
+                               /*AllowExplicit=*/AllowExplicitConv);
+      }
     }
   }
 
@@ -3122,8 +3134,8 @@ static void TryListInitialization(Sema &S,
 static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
                                              const InitializedEntity &Entity,
                                              const InitializationKind &Kind,
-                                                          Expr *Initializer,
-                                                          bool AllowRValues,
+                                             Expr *Initializer,
+                                             bool AllowRValues,
                                              InitializationSequence &Sequence) {
   QualType DestType = Entity.getType();
   QualType cv1T1 = DestType->getAs<ReferenceType>()->getPointeeType();
@@ -3151,7 +3163,8 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
   // Determine whether we are allowed to call explicit constructors or
   // explicit conversion operators.
   bool AllowExplicit = Kind.AllowExplicit();
-
+  bool AllowExplicitConvs = Kind.allowExplicitConversionFunctions();
+  
   const RecordType *T1RecordType = 0;
   if (AllowRValues && (T1RecordType = T1->getAs<RecordType>()) &&
       !S.RequireCompleteType(Kind.getLocation(), T1, 0)) {
@@ -3220,7 +3233,7 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
       // FIXME: Do we need to make sure that we only consider conversion
       // candidates with reference-compatible results? That might be needed to
       // break recursion.
-      if ((AllowExplicit || !Conv->isExplicit()) &&
+      if ((AllowExplicitConvs || !Conv->isExplicit()) &&
           (AllowRValues || Conv->getConversionType()->isLValueReferenceType())){
         if (ConvTemplate)
           S.AddTemplateConversionCandidate(ConvTemplate, I.getPair(),
@@ -4636,10 +4649,21 @@ PerformConstructorInitialization(Sema &S,
 
   ExprResult CurInit = S.Owned((Expr *)0);
 
+  // C++ [over.match.copy]p1:
+  //   - When initializing a temporary to be bound to the first parameter 
+  //     of a constructor that takes a reference to possibly cv-qualified 
+  //     T as its first argument, called with a single argument in the 
+  //     context of direct-initialization, explicit conversion functions
+  //     are also considered.
+  bool AllowExplicitConv = Kind.AllowExplicit() && !Kind.isCopyInit() &&
+                           Args.size() == 1 && 
+                           Constructor->isCopyOrMoveConstructor();
+
   // Determine the arguments required to actually perform the constructor
   // call.
   if (S.CompleteConstructorCall(Constructor, move(Args),
-                                Loc, ConstructorArgs))
+                                Loc, ConstructorArgs,
+                                AllowExplicitConv))
     return ExprError();
 
 
@@ -6097,7 +6121,8 @@ ExprResult
 Sema::PerformCopyInitialization(const InitializedEntity &Entity,
                                 SourceLocation EqualLoc,
                                 ExprResult Init,
-                                bool TopLevelOfInitList) {
+                                bool TopLevelOfInitList,
+                                bool AllowExplicit) {
   if (Init.isInvalid())
     return ExprError();
 
@@ -6108,7 +6133,8 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity,
     EqualLoc = InitE->getLocStart();
 
   InitializationKind Kind = InitializationKind::CreateCopy(InitE->getLocStart(),
-                                                           EqualLoc);
+                                                           EqualLoc,
+                                                           AllowExplicit);
   InitializationSequence Seq(*this, Entity, Kind, &InitE, 1);
   Init.release();
 
index 2b567b3984220fe66cafde78cbd02169c29d9799..c7f33943437b48b3f71a048233b3ac30b510f0a3 100644 (file)
@@ -4213,7 +4213,8 @@ static ImplicitConversionSequence
 TryCopyInitialization(Sema &S, Expr *From, QualType ToType,
                       bool SuppressUserConversions,
                       bool InOverloadResolution,
-                      bool AllowObjCWritebackConversion);
+                      bool AllowObjCWritebackConversion,
+                      bool AllowExplicit = false);
 
 /// TryListConversion - Try to copy-initialize a value of type ToType from the
 /// initializer list From.
@@ -4413,7 +4414,8 @@ static ImplicitConversionSequence
 TryCopyInitialization(Sema &S, Expr *From, QualType ToType,
                       bool SuppressUserConversions,
                       bool InOverloadResolution,
-                      bool AllowObjCWritebackConversion) {
+                      bool AllowObjCWritebackConversion,
+                      bool AllowExplicit) {
   if (InitListExpr *FromInitList = dyn_cast<InitListExpr>(From))
     return TryListConversion(S, FromInitList, ToType, SuppressUserConversions,
                              InOverloadResolution,AllowObjCWritebackConversion);
@@ -4422,7 +4424,7 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType,
     return TryReferenceInit(S, From, ToType,
                             /*FIXME:*/From->getLocStart(),
                             SuppressUserConversions,
-                            /*AllowExplicit=*/false);
+                            AllowExplicit);
 
   return TryImplicitConversion(S, From, ToType,
                                SuppressUserConversions,
@@ -5103,7 +5105,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
                            Expr **Args, unsigned NumArgs,
                            OverloadCandidateSet& CandidateSet,
                            bool SuppressUserConversions,
-                           bool PartialOverloading) {
+                           bool PartialOverloading,
+                           bool AllowExplicit) {
   const FunctionProtoType* Proto
     = dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
   assert(Proto && "Functions without a prototype cannot be overloaded");
@@ -5204,7 +5207,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
                                 SuppressUserConversions,
                                 /*InOverloadResolution=*/true,
                                 /*AllowObjCWritebackConversion=*/
-                                  getLangOptions().ObjCAutoRefCount);
+                                  getLangOptions().ObjCAutoRefCount,
+                                AllowExplicit);
       if (Candidate.Conversions[ArgIdx].isBad()) {
         Candidate.Viable = false;
         Candidate.FailureKind = ovl_fail_bad_conversion;
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.copy/p1.cpp
new file mode 100644 (file)
index 0000000..8b8a1ef
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
+
+namespace ExplicitConv {
+  struct X { }; // expected-note 2{{candidate constructor}}
+
+  struct Y {
+    explicit operator X() const;
+  };
+
+  void test(const Y& y) {
+    X x(static_cast<X>(y));
+    X x2((X)y);
+    X x3 = y; // expected-error{{no viable conversion from 'const ExplicitConv::Y' to 'ExplicitConv::X'}}
+  }
+}
index 12f0ca2643efcafae745a37bba42c8071c8797a1..477463771e8869c9a3422d4be7a6fc22c3896f09 100644 (file)
@@ -43,16 +43,8 @@ namespace Conversion {
     class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \
                                                  expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \
                     expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y &' for 1st argument}} \
-                    expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \
-                                       expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} \
-                                       expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Conversion::Z' to 'const Conversion::Y' for 1st argument}} \
-          expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}} \
-                                       expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
+          expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Conversion::Z' to 'Conversion::Y &&' for 1st argument}}
+
     struct Z {
       explicit operator Y() const;
       explicit operator int() const;
@@ -61,10 +53,9 @@ namespace Conversion {
     Z z;
     // 13.3.1.4p1 & 8.5p16:
     Y y2 = z; // expected-error {{no viable conversion from 'Conversion::Z' to 'Conversion::Y'}}
-    // FIXME: These are well-formed per C++0x 13.3.1.4p1 (see DR899).
-    Y y3 = (Y)z; // expected-error {{no matching conversion for C-style cast from 'Conversion::Z' to 'Conversion::Y'}}
-    Y y4 = Y(z); // expected-error {{no matching conversion for functional-style cast from 'Conversion::Z' to 'Conversion::Y'}}
-    Y y5 = static_cast<Y>(z); // expected-error {{no matching conversion for static_cast from 'Conversion::Z' to 'Conversion::Y'}}
+    Y y3 = (Y)z;
+    Y y4 = Y(z);
+    Y y5 = static_cast<Y>(z);
     // 13.3.1.5p1 & 8.5p16:
     int i1 = (int)z;
     int i2 = int(z);