r191150 - PR17295: Do not allow explicit conversion functions to be used in cases where

Richard Smith richard-llvm at metafoo.co.uk
Sat Sep 21 14:55:46 PDT 2013


Author: rsmith
Date: Sat Sep 21 16:55:46 2013
New Revision: 191150

URL: http://llvm.org/viewvc/llvm-project?rev=191150&view=rev
Log:
PR17295: Do not allow explicit conversion functions to be used in cases where
an additional conversion (other than a qualification conversion) would be
required after the explicit conversion.

Conversely, do allow explicit conversion functions to be used when initializing
a temporary for a reference binding in direct-list-initialization.

Modified:
    cfe/trunk/include/clang/Sema/Initialization.h
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/test/SemaCXX/explicit.cpp

Modified: cfe/trunk/include/clang/Sema/Initialization.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Initialization.h?rev=191150&r1=191149&r2=191150&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Initialization.h (original)
+++ cfe/trunk/include/clang/Sema/Initialization.h Sat Sep 21 16:55:46 2013
@@ -853,6 +853,10 @@ public:
                          const InitializationKind &Kind,
                          MultiExprArg Args,
                          bool InInitList = false);
+  void InitializeFrom(Sema &S, const InitializedEntity &Entity,
+                      const InitializationKind &Kind, MultiExprArg Args,
+                      bool InInitList);
+
   ~InitializationSequence();
   
   /// \brief Perform the actual initialization of the given entity based on

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=191150&r1=191149&r2=191150&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Sat Sep 21 16:55:46 2013
@@ -3346,6 +3346,33 @@ static void TryListInitialization(Sema &
       return;
     }
   }
+  if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() &&
+      InitList->getNumInits() == 1 &&
+      InitList->getInit(0)->getType()->isRecordType()) {
+    //   - Otherwise, if the initializer list has a single element of type E
+    //     [...references are handled above...], the object or reference is
+    //     initialized from that element; if a narrowing conversion is required
+    //     to convert the element to T, the program is ill-formed.
+    //
+    // Per core-24034, this is direct-initialization if we were performing
+    // direct-list-initialization and copy-initialization otherwise.
+    // We can't use InitListChecker for this, because it always performs
+    // copy-initialization. This only matters if we might use an 'explicit'
+    // conversion operator, so we only need to handle the cases where the source
+    // is of record type.
+    InitializationKind SubKind =
+        Kind.getKind() == InitializationKind::IK_DirectList
+            ? InitializationKind::CreateDirect(Kind.getLocation(),
+                                               InitList->getLBraceLoc(),
+                                               InitList->getRBraceLoc())
+            : Kind;
+    Expr *SubInit[1] = { InitList->getInit(0) };
+    Sequence.InitializeFrom(S, Entity, SubKind, SubInit,
+                            /*TopLevelOfInitList*/true);
+    if (Sequence)
+      Sequence.RewrapReferenceInitList(Entity.getType(), InitList);
+    return;
+  }
 
   InitListChecker CheckInitList(S, Entity, InitList,
           DestType, /*VerifyOnly=*/true);
@@ -4366,6 +4393,14 @@ InitializationSequence::InitializationSe
                                                MultiExprArg Args,
                                                bool TopLevelOfInitList)
     : FailedCandidateSet(Kind.getLocation()) {
+  InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList);
+}
+
+void InitializationSequence::InitializeFrom(Sema &S,
+                                            const InitializedEntity &Entity,
+                                            const InitializationKind &Kind,
+                                            MultiExprArg Args,
+                                            bool TopLevelOfInitList) {
   ASTContext &Context = S.Context;
 
   // Eliminate non-overload placeholder types in the arguments.  We

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=191150&r1=191149&r2=191150&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Sat Sep 21 16:55:46 2013
@@ -5829,6 +5829,17 @@ Sema::AddConversionCandidate(CXXConversi
     ConvType = Conversion->getConversionType().getNonReferenceType();
   }
 
+  // Per C++ [over.match.conv]p1, [over.match.ref]p1, an explicit conversion
+  // operator is only a candidate if its return type is the target type or
+  // can be converted to the target type with a qualification conversion.
+  bool ObjCLifetimeConversion;
+  QualType ToNonRefType = ToType.getNonReferenceType();
+  if (Conversion->isExplicit() &&
+      !Context.hasSameUnqualifiedType(ConvType, ToNonRefType) &&
+      !IsQualificationConversion(ConvType, ToNonRefType, /*CStyle*/false,
+                                 ObjCLifetimeConversion))
+    return;
+
   // Overload resolution is always an unevaluated context.
   EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated);
 

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=191150&r1=191149&r2=191150&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Sat Sep 21 16:55:46 2013
@@ -3372,7 +3372,9 @@ void Sema::BuildVariableInstantiation(
       OldVar->isPreviousDeclInSameBlockScope());
   NewVar->setAccess(OldVar->getAccess());
 
-  if (!OldVar->isStaticDataMember()) {
+  // For local variables, inherit the 'used' and 'referenced' flags from the
+  // primary template.
+  if (OldVar->getLexicalDeclContext()->isFunctionOrMethod()) {
     NewVar->setIsUsed(OldVar->isUsed(false));
     NewVar->setReferenced(OldVar->isReferenced());
   }

Modified: cfe/trunk/test/SemaCXX/explicit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/explicit.cpp?rev=191150&r1=191149&r2=191150&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/explicit.cpp (original)
+++ cfe/trunk/test/SemaCXX/explicit.cpp Sat Sep 21 16:55:46 2013
@@ -4,11 +4,11 @@ struct A {
   A(int);
 };
 
-struct B {
+struct B { // expected-note+ {{candidate}}
   explicit B(int);
 };
 
-B::B(int) { }
+B::B(int) { } // expected-note+ {{here}}
 
 struct C {
   void f(const A&);
@@ -18,6 +18,22 @@ struct C {
 void f(C c) {
   c.f(10);
 }
+
+A a0 = 0;
+A a1(0);
+A &&a2 = 0;
+A &&a3(0);
+A a4{0};
+A &&a5 = {0};
+A &&a6{0};
+
+B b0 = 0; // expected-error {{no viable conversion}}
+B b1(0);
+B &&b2 = 0; // expected-error {{could not bind}}
+B &&b3(0); // expected-error {{could not bind}}
+B b4{0};
+B &&b5 = {0}; // expected-error {{chosen constructor is explicit}}
+B &&b6{0};
 }
 
 namespace Conversion {
@@ -40,12 +56,11 @@ namespace Conversion {
   void testExplicit()
   {
     // Taken from 12.3.2p2
-    class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \
-					          expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}} \
-                    expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \
-          expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}}
+    class X { X(); }; // expected-note+ {{candidate constructor}}
+    class Y { }; // expected-note+ {{candidate constructor (the implicit}}
 
     struct Z {
+      explicit operator X() const;
       explicit operator Y() const;
       explicit operator int() const;
     };
@@ -53,6 +68,7 @@ namespace Conversion {
     Z z;
     // 13.3.1.4p1 & 8.5p16:
     Y y2 = z; // expected-error {{no viable conversion from 'Z' to 'Y'}}
+    Y y2b(z);
     Y y3 = (Y)z;
     Y y4 = Y(z);
     Y y5 = static_cast<Y>(z);
@@ -63,7 +79,24 @@ namespace Conversion {
     int i4(z);
     // 13.3.1.6p1 & 8.5.3p5:
     const Y& y6 = z; // expected-error {{no viable conversion from 'Z' to 'const Y'}}
-    const int& y7(z);
+    const int& y7 = z; // expected-error {{no viable conversion from 'Z' to 'const int'}}
+    const Y& y8(z);
+    const int& y9(z);
+
+    // Y is an aggregate, so aggregate-initialization is performed and the
+    // conversion function is not considered.
+    const Y y10{z}; // expected-error {{excess elements}}
+    const Y& y11{z}; // expected-error {{no viable conversion from 'Z' to 'const Y'}}
+    const int& y12{z};
+
+    // X is not an aggregate, so constructors are considered.
+    // However, by 13.3.3.1/4, only standard conversion sequences and
+    // ellipsis conversion sequences are considered here, so this is not
+    // allowed.
+    // FIXME: It's not really clear that this is a sensible restriction for this
+    // case. g++ allows this, EDG does not.
+    const X x1{z}; // expected-error {{no matching constructor}}
+    const X& x2{z}; // expected-error {{no matching constructor}}
   }
   
   void testBool() {
@@ -119,6 +152,40 @@ namespace Conversion {
     // 6.5.3:
     for (;b;) {}
     for (;n;) {}
+
+    // 13.3.1.5p1:
+    bool direct1(b);
+    bool direct2(n);
+    int direct3(b);
+    int direct4(n); // expected-error {{no viable conversion}}
+    const bool &direct5(b);
+    const bool &direct6(n);
+    const int &direct7(b);
+    const int &direct8(n); // expected-error {{no viable conversion}}
+    bool directList1{b};
+    bool directList2{n};
+    int directList3{b};
+    int directList4{n}; // expected-error {{no viable conversion}}
+    const bool &directList5{b};
+    const bool &directList6{n};
+    const int &directList7{b};
+    const int &directList8{n}; // expected-error {{no viable conversion}}
+    bool copy1 = b;
+    bool copy2 = n; // expected-error {{no viable conversion}}
+    int copy3 = b;
+    int copy4 = n; // expected-error {{no viable conversion}}
+    const bool &copy5 = b;
+    const bool &copy6 = n; // expected-error {{no viable conversion}}
+    const int &copy7 = b;
+    const int &copy8 = n; // expected-error {{no viable conversion}}
+    bool copyList1 = {b};
+    bool copyList2 = {n}; // expected-error {{no viable conversion}}
+    int copyList3 = {b};
+    int copyList4 = {n}; // expected-error {{no viable conversion}}
+    const bool &copyList5 = {b};
+    const bool &copyList6 = {n}; // expected-error {{no viable conversion}}
+    const int &copyList7 = {b};
+    const int &copyList8 = {n}; // expected-error {{no viable conversion}}
   }
   
   void testNew()





More information about the cfe-commits mailing list