r288866 - [c++17] P0135R1: Guaranteed copy elision.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 6 15:52:29 PST 2016


Author: rsmith
Date: Tue Dec  6 17:52:28 2016
New Revision: 288866

URL: http://llvm.org/viewvc/llvm-project?rev=288866&view=rev
Log:
[c++17] P0135R1: Guaranteed copy elision.

When an object of class type is initialized from a prvalue of the same type
(ignoring cv qualifications), use the prvalue to initialize the object directly
instead of inserting a redundant elidable call to a copy constructor.

Added:
    cfe/trunk/test/CodeGenCXX/cxx1z-copy-omission.cpp
    cfe/trunk/test/SemaCXX/cxx1z-copy-omission.cpp
Modified:
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/lib/AST/Expr.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CGExprAgg.cpp
    cfe/trunk/lib/CodeGen/CGExprConstant.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/CXX/drs/dr0xx.cpp
    cfe/trunk/test/CXX/drs/dr10xx.cpp
    cfe/trunk/test/CXX/drs/dr1xx.cpp
    cfe/trunk/test/CXX/drs/dr4xx.cpp
    cfe/trunk/test/SemaCXX/aggregate-initialization.cpp
    cfe/trunk/www/cxx_dr_status.html
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Tue Dec  6 17:52:28 2016
@@ -3786,7 +3786,7 @@ public:
 
   /// \brief Build an empty initializer list.
   explicit InitListExpr(EmptyShell Empty)
-    : Expr(InitListExprClass, Empty) { }
+    : Expr(InitListExprClass, Empty), AltForm(nullptr, true) { }
 
   unsigned getNumInits() const { return InitExprs.size(); }
 
@@ -3894,6 +3894,11 @@ public:
   // literal or an @encode?
   bool isStringLiteralInit() const;
 
+  /// Is this a transparent initializer list (that is, an InitListExpr that is
+  /// purely syntactic, and whose semantics are that of the sole contained
+  /// initializer)?
+  bool isTransparent() const;
+
   SourceLocation getLBraceLoc() const { return LBraceLoc; }
   void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; }
   SourceLocation getRBraceLoc() const { return RBraceLoc; }

Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Tue Dec  6 17:52:28 2016
@@ -1864,6 +1864,24 @@ bool InitListExpr::isStringLiteralInit()
   return isa<StringLiteral>(Init) || isa<ObjCEncodeExpr>(Init);
 }
 
+bool InitListExpr::isTransparent() const {
+  assert(isSemanticForm() && "syntactic form never semantically transparent");
+
+  // A glvalue InitListExpr is always just sugar.
+  if (isGLValue()) {
+    assert(getNumInits() == 1 && "multiple inits in glvalue init list");
+    return true;
+  }
+
+  // Otherwise, we're sugar if and only if we have exactly one initializer that
+  // is of the same type.
+  if (getNumInits() != 1 || !getInit(0))
+    return false;
+
+  return getType().getCanonicalType() ==
+         getInit(0)->getType().getCanonicalType();
+}
+
 SourceLocation InitListExpr::getLocStart() const {
   if (InitListExpr *SyntacticForm = getSyntacticForm())
     return SyntacticForm->getLocStart();
@@ -2246,12 +2264,15 @@ bool Expr::isUnusedResultAWarning(const
     // effects (e.g. a placement new with an uninitialized POD).
   case CXXDeleteExprClass:
     return false;
+  case MaterializeTemporaryExprClass:
+    return cast<MaterializeTemporaryExpr>(this)->GetTemporaryExpr()
+               ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
   case CXXBindTemporaryExprClass:
-    return (cast<CXXBindTemporaryExpr>(this)
-            ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
+    return cast<CXXBindTemporaryExpr>(this)->getSubExpr()
+               ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
   case ExprWithCleanupsClass:
-    return (cast<ExprWithCleanups>(this)
-            ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
+    return cast<ExprWithCleanups>(this)->getSubExpr()
+               ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
   }
 }
 

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Tue Dec  6 17:52:28 2016
@@ -5670,6 +5670,9 @@ bool RecordExprEvaluator::VisitCastExpr(
 }
 
 bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
+  if (E->isTransparent())
+    return Visit(E->getInit(0));
+
   const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
   if (RD->isInvalidDecl()) return false;
   const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Tue Dec  6 17:52:28 2016
@@ -3522,7 +3522,7 @@ LValue CodeGenFunction::EmitInitListLVal
     return EmitAggExprToLValue(E);
 
   // An lvalue initializer list must be initializing a reference.
-  assert(E->getNumInits() == 1 && "reference init with multiple values");
+  assert(E->isTransparent() && "non-transparent glvalue init list");
   return EmitLValue(E->getInit(0));
 }
 

Modified: cfe/trunk/lib/CodeGen/CGExprAgg.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprAgg.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprAgg.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprAgg.cpp Tue Dec  6 17:52:28 2016
@@ -1145,15 +1145,15 @@ void AggExprEmitter::VisitInitListExpr(I
   if (E->hadArrayRangeDesignator())
     CGF.ErrorUnsupported(E, "GNU array range designator extension");
 
+  if (E->isTransparent())
+    return Visit(E->getInit(0));
+
   AggValueSlot Dest = EnsureSlot(E->getType());
 
   LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
 
   // Handle initialization of an array.
   if (E->getType()->isArrayType()) {
-    if (E->isStringLiteralInit())
-      return Visit(E->getInit(0));
-
     QualType elementType =
         CGF.getContext().getAsArrayType(E->getType())->getElementType();
 
@@ -1162,16 +1162,6 @@ void AggExprEmitter::VisitInitListExpr(I
     return;
   }
 
-  if (E->getType()->isAtomicType()) {
-    // An _Atomic(T) object can be list-initialized from an expression
-    // of the same type.
-    assert(E->getNumInits() == 1 &&
-           CGF.getContext().hasSameUnqualifiedType(E->getInit(0)->getType(),
-                                                   E->getType()) &&
-           "unexpected list initialization for atomic object");
-    return Visit(E->getInit(0));
-  }
-
   assert(E->getType()->isRecordType() && "Only support structs/unions here!");
 
   // Do struct initialization; this code just sets each individual member

Modified: cfe/trunk/lib/CodeGen/CGExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprConstant.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprConstant.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprConstant.cpp Tue Dec  6 17:52:28 2016
@@ -778,9 +778,6 @@ public:
   }
 
   llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) {
-    if (ILE->isStringLiteralInit())
-      return Visit(ILE->getInit(0));
-
     llvm::ArrayType *AType =
         cast<llvm::ArrayType>(ConvertType(ILE->getType()));
     llvm::Type *ElemTy = AType->getElementType();
@@ -845,6 +842,9 @@ public:
   }
 
   llvm::Constant *VisitInitListExpr(InitListExpr *ILE) {
+    if (ILE->isTransparent())
+      return Visit(ILE->getInit(0));
+
     if (ILE->getType()->isArrayType())
       return EmitArrayInitialization(ILE);
 

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Tue Dec  6 17:52:28 2016
@@ -6836,6 +6836,16 @@ ExprResult Sema::IgnoredValueConversions
         return E;
       E = Res.get();
     }
+
+    // C++1z:
+    //   If the expression is a prvalue after this optional conversion, the
+    //   temporary materialization conversion is applied.
+    //
+    // We skip this step: IR generation is able to synthesize the storage for
+    // itself in the aggregate case, and adding the extra node to the AST is
+    // just clutter.
+    // FIXME: We don't emit lifetime markers for the temporaries due to this.
+    // FIXME: Do any other AST consumers care about this?
     return E;
   }
 

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Tue Dec  6 17:52:28 2016
@@ -3546,8 +3546,14 @@ static void TryConstructorInitialization
                                          InitializationSequence &Sequence,
                                          bool IsListInit = false,
                                          bool IsInitListCopy = false) {
-  assert((!IsListInit || (Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
-         "IsListInit must come with a single initializer list argument.");
+  assert(((!IsListInit && !IsInitListCopy) ||
+          (Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
+         "IsListInit/IsInitListCopy must come with a single initializer list "
+         "argument.");
+  InitListExpr *ILE =
+      (IsListInit || IsInitListCopy) ? cast<InitListExpr>(Args[0]) : nullptr;
+  MultiExprArg UnwrappedArgs =
+      ILE ? MultiExprArg(ILE->getInits(), ILE->getNumInits()) : Args;
 
   // The type we're constructing needs to be complete.
   if (!S.isCompleteType(Kind.getLocation(), DestType)) {
@@ -3555,6 +3561,35 @@ static void TryConstructorInitialization
     return;
   }
 
+  // C++1z [dcl.init]p17:
+  //     - If the initializer expression is a prvalue and the cv-unqualified
+  //       version of the source type is the same class as the class of the
+  //       destination, the initializer expression is used to initialize the
+  //       destination object.
+  // Per DR (no number yet), this does not apply when initializing a base
+  // class or delegating to another constructor from a mem-initializer.
+  if (S.getLangOpts().CPlusPlus1z &&
+      Entity.getKind() != InitializedEntity::EK_Base &&
+      Entity.getKind() != InitializedEntity::EK_Delegating &&
+      UnwrappedArgs.size() == 1 && UnwrappedArgs[0]->isRValue() &&
+      S.Context.hasSameUnqualifiedType(UnwrappedArgs[0]->getType(), DestType)) {
+    // Convert qualifications if necessary.
+    QualType InitType = UnwrappedArgs[0]->getType();
+    ImplicitConversionSequence ICS;
+    ICS.setStandard();
+    ICS.Standard.setAsIdentityConversion();
+    ICS.Standard.setFromType(InitType);
+    ICS.Standard.setAllToTypes(InitType);
+    if (!S.Context.hasSameType(InitType, DestType)) {
+      ICS.Standard.Third = ICK_Qualification;
+      ICS.Standard.setToType(2, DestType);
+    }
+    Sequence.AddConversionSequenceStep(ICS, DestType);
+    if (ILE)
+      Sequence.RewrapReferenceInitList(DestType, ILE);
+    return;
+  }
+
   const RecordType *DestRecordType = DestType->getAs<RecordType>();
   assert(DestRecordType && "Constructor initialization requires record type");
   CXXRecordDecl *DestRecordDecl
@@ -3588,20 +3623,16 @@ static void TryConstructorInitialization
   //     constructors of the class T and the argument list consists of the
   //     initializer list as a single argument.
   if (IsListInit) {
-    InitListExpr *ILE = cast<InitListExpr>(Args[0]);
     AsInitializerList = true;
 
     // If the initializer list has no elements and T has a default constructor,
     // the first phase is omitted.
-    if (ILE->getNumInits() != 0 || !DestRecordDecl->hasDefaultConstructor())
+    if (!(UnwrappedArgs.empty() && DestRecordDecl->hasDefaultConstructor()))
       Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
                                           CandidateSet, Ctors, Best,
                                           CopyInitialization, AllowExplicit,
                                           /*OnlyListConstructor=*/true,
                                           IsListInit);
-
-    // Time to unwrap the init list.
-    Args = MultiExprArg(ILE->getInits(), ILE->getNumInits());
   }
 
   // C++11 [over.match.list]p1:
@@ -3611,7 +3642,7 @@ static void TryConstructorInitialization
   //     elements of the initializer list.
   if (Result == OR_No_Viable_Function) {
     AsInitializerList = false;
-    Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
+    Result = ResolveConstructorOverload(S, Kind.getLocation(), UnwrappedArgs,
                                         CandidateSet, Ctors, Best,
                                         CopyInitialization, AllowExplicit,
                                         /*OnlyListConstructors=*/false,
@@ -3821,8 +3852,8 @@ static void TryListInitialization(Sema &
       QualType InitType = InitList->getInit(0)->getType();
       if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
           S.IsDerivedFrom(InitList->getLocStart(), InitType, DestType)) {
-        Expr *InitAsExpr = InitList->getInit(0);
-        TryConstructorInitialization(S, Entity, Kind, InitAsExpr, DestType,
+        Expr *InitListAsExpr = InitList;
+        TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
                                      Sequence, /*InitListSyntax*/ false,
                                      /*IsInitListCopy*/ true);
         return;
@@ -4332,16 +4363,21 @@ static void TryReferenceInitializationCo
   }
 
   //    - If the initializer expression
+  // C++14-and-before:
   //      - is an xvalue, class prvalue, array prvalue, or function lvalue and
   //        "cv1 T1" is reference-compatible with "cv2 T2"
+  // C++1z:
+  //      - is an rvalue or function lvalue and "cv1 T1" is reference-compatible
+  //        with "cv2 T2"
   // Note: functions are handled below.
   if (!T1Function &&
       (RefRelationship == Sema::Ref_Compatible ||
        (Kind.isCStyleOrFunctionalCast() &&
         RefRelationship == Sema::Ref_Related)) &&
       (InitCategory.isXValue() ||
-       (InitCategory.isPRValue() && T2->isRecordType()) ||
-       (InitCategory.isPRValue() && T2->isArrayType()))) {
+       (InitCategory.isPRValue() &&
+        (S.getLangOpts().CPlusPlus1z || T2->isRecordType() ||
+         T2->isArrayType())))) {
     ExprValueKind ValueKind = InitCategory.isXValue()? VK_XValue : VK_RValue;
     if (InitCategory.isPRValue() && T2->isRecordType()) {
       // The corresponding bullet in C++03 [dcl.init.ref]p5 gives the
@@ -6604,7 +6640,26 @@ InitializationSequence::Perform(Sema &S,
         CreatedObject = Conversion->getReturnType()->isRecordType();
       }
 
+      // C++14 and before:
+      //   - if the function is a constructor, the call initializes a temporary
+      //     of the cv-unqualified version of the destination type [...]
+      // C++1z:
+      //   - if the function is a constructor, the call is a prvalue of the
+      //     cv-unqualified version of the destination type whose return object
+      //     is initialized by the constructor [...]
+      // Both:
+      //     The [..] call is used to direct-initialize, according to the rules
+      //     above, the object that is the destination of the
+      //     copy-initialization.
+      // In C++14 and before, that always means the "constructors are
+      // considered" bullet, because we have arrived at a reference-related
+      // type. In C++1z, it only means that if the types are different or we
+      // didn't produce a prvalue, so just check for that case here.
       bool RequiresCopy = !IsCopy && !isReferenceBinding(Steps.back());
+      if (S.getLangOpts().CPlusPlus1z && CurInit.get()->isRValue() &&
+          S.Context.hasSameUnqualifiedType(
+              Entity.getType().getNonReferenceType(), CurInit.get()->getType()))
+        RequiresCopy = false;
       bool MaybeBindToTemp = RequiresCopy || shouldBindAsTemporary(Entity);
 
       if (!MaybeBindToTemp && CreatedObject && shouldDestroyTemporary(Entity)) {

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Dec  6 17:52:28 2016
@@ -4979,7 +4979,7 @@ TryObjectArgumentInitialization(Sema &S,
   // cv-qualification on the member function declaration.
   //
   // However, when finding an implicit conversion sequence for the argument, we
-  // are not allowed to create temporaries or perform user-defined conversions
+  // are not allowed to perform user-defined conversions
   // (C++ [over.match.funcs]p5). We perform a simplified version of
   // reference binding here, that allows class rvalues to bind to
   // non-constant references.

Modified: cfe/trunk/test/CXX/drs/dr0xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr0xx.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr0xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr0xx.cpp Tue Dec  6 17:52:28 2016
@@ -248,7 +248,7 @@ namespace dr20 { // dr20: yes
   private:
     X(const X&); // expected-note {{here}}
   };
-  X f();
+  X &f();
   X x = f(); // expected-error {{private}}
 }
 
@@ -316,8 +316,15 @@ namespace dr25 { // dr25: yes
 namespace dr26 { // dr26: yes
   struct A { A(A, const A & = A()); }; // expected-error {{must pass its first argument by reference}}
   struct B {
-    B(); // expected-note {{candidate}}
-    B(const B &, B = B()); // expected-error {{no matching constructor}} expected-note {{candidate}} expected-note {{here}}
+    B(); // expected-note 0-1{{candidate}}
+    B(const B &, B = B());
+#if __cplusplus <= 201402L
+    // expected-error at -2 {{no matching constructor}} expected-note at -2 {{candidate}} expected-note at -2 {{here}}
+#endif
+  };
+  struct C {
+    static C &f();
+    C(const C &, C = f()); // expected-error {{no matching constructor}} expected-note {{candidate}} expected-note {{here}}
   };
 }
 
@@ -662,25 +669,33 @@ namespace dr58 { // dr58: yes
 
 namespace dr59 { // dr59: yes
   template<typename T> struct convert_to { operator T() const; };
-  struct A {}; // expected-note 2{{volatile qualifier}} expected-note 2{{requires 0 arguments}}
-  struct B : A {}; // expected-note 2{{volatile qualifier}} expected-note 2{{requires 0 arguments}}
-#if __cplusplus >= 201103L // move constructors
-  // expected-note at -3 2{{volatile qualifier}}
-  // expected-note at -3 2{{volatile qualifier}}
-#endif
+  struct A {}; // expected-note 5+{{candidate}}
+  struct B : A {}; // expected-note 0+{{candidate}}
 
   A a1 = convert_to<A>();
   A a2 = convert_to<A&>();
   A a3 = convert_to<const A>();
-  A a4 = convert_to<const volatile A>(); // expected-error {{no viable}}
+  A a4 = convert_to<const volatile A>();
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{no viable}}
+#endif
   A a5 = convert_to<const volatile A&>(); // expected-error {{no viable}}
 
   B b1 = convert_to<B>();
   B b2 = convert_to<B&>();
   B b3 = convert_to<const B>();
-  B b4 = convert_to<const volatile B>(); // expected-error {{no viable}}
+  B b4 = convert_to<const volatile B>();
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{no viable}}
+#endif
   B b5 = convert_to<const volatile B&>(); // expected-error {{no viable}}
 
+  A c1 = convert_to<B>();
+  A c2 = convert_to<B&>();
+  A c3 = convert_to<const B>();
+  A c4 = convert_to<const volatile B>(); // expected-error {{no viable}}
+  A c5 = convert_to<const volatile B&>(); // expected-error {{no viable}}
+
   int n1 = convert_to<int>();
   int n2 = convert_to<int&>();
   int n3 = convert_to<const int>();
@@ -920,14 +935,17 @@ namespace dr84 { // dr84: yes
   struct A { operator B() const; };
   struct C {};
   struct B {
-    B(B&); // expected-note {{candidate}}
-    B(C); // expected-note {{no known conversion from 'dr84::B' to 'dr84::C'}}
+    B(B&); // expected-note 0-1{{candidate}}
+    B(C); // expected-note 0-1{{no known conversion from 'dr84::B' to 'dr84::C'}}
     operator C() const;
   };
   A a;
   // Cannot use B(C) / operator C() pair to construct the B from the B temporary
-  // here.
-  B b = a; // expected-error {{no viable}}
+  // here. In C++1z, we initialize the B object directly using 'A::operator B()'.
+  B b = a;
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{no viable}}
+#endif
 }
 
 namespace dr85 { // dr85: yes

Modified: cfe/trunk/test/CXX/drs/dr10xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr10xx.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr10xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr10xx.cpp Tue Dec  6 17:52:28 2016
@@ -3,8 +3,6 @@
 // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 
-// expected-no-diagnostics
-
 namespace std {
   __extension__ typedef __SIZE_TYPE__ size_t;
 
@@ -32,6 +30,18 @@ namespace dr1048 { // dr1048: 3.6
 #endif
 }
 
+namespace dr1054 { // dr1054: no
+  // FIXME: Test is incomplete.
+  struct A {} volatile a;
+  void f() {
+    // FIXME: This is wrong: an lvalue-to-rvalue conversion is applied here,
+    // which copy-initializes a temporary from 'a'. Therefore this is
+    // ill-formed because A does not have a volatile copy constructor.
+    // (We might want to track this aspect under dr1383 instead?)
+    a; // expected-warning {{assign into a variable to force a volatile load}}
+  }
+}
+
 namespace dr1070 { // dr1070: 3.5
 #if __cplusplus >= 201103L
   struct A {

Modified: cfe/trunk/test/CXX/drs/dr1xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr1xx.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr1xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr1xx.cpp Tue Dec  6 17:52:28 2016
@@ -576,11 +576,18 @@ namespace dr151 { // dr151: yes
 
 namespace dr152 { // dr152: yes
   struct A {
-    A(); // expected-note {{not viable}}
+    A(); // expected-note 0-2{{not viable}}
     explicit A(const A&);
   };
-  A a1 = A(); // expected-error {{no matching constructor}}
+  A a1 = A();
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{no matching constructor}}
+#endif
   A a2((A()));
+
+  A &f();
+  A a3 = f(); // expected-error {{no matching constructor}}
+  A a4(f());
 }
 
 // dr153: na
@@ -823,11 +830,20 @@ namespace dr176 { // dr176: yes
 namespace dr177 { // dr177: yes
   struct B {};
   struct A {
-    A(A &); // expected-note {{not viable: expects an l-value}}
-    A(const B &); // expected-note {{not viable: no known conversion from 'dr177::A' to}}
+    A(A &); // expected-note 0-1{{not viable: expects an l-value}}
+    A(const B &); // expected-note 0-1{{not viable: no known conversion from 'dr177::A' to}}
   };
   B b;
-  A a = b; // expected-error {{no viable constructor copying variable}}
+  A a = b;
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{no viable constructor copying variable}}
+#endif
+
+  struct C { C(C&); }; // expected-note {{not viable: no known conversion from 'dr177::D' to 'dr177::C &'}}
+  struct D : C {};
+  struct E { operator D(); };
+  E e;
+  C c = e; // expected-error {{no viable constructor copying variable of type 'dr177::D'}}
 }
 
 namespace dr178 { // dr178: yes

Modified: cfe/trunk/test/CXX/drs/dr4xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr4xx.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr4xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr4xx.cpp Tue Dec  6 17:52:28 2016
@@ -553,12 +553,21 @@ namespace dr446 { // dr446: yes
     void(b ? a : a);
     b ? A() : a; // expected-error {{deleted}}
     b ? a : A(); // expected-error {{deleted}}
-    b ? A() : A(); // expected-error {{deleted}}
+    b ? A() : A();
+#if __cplusplus <= 201402L
+    // expected-error at -2 {{deleted}}
+#endif
 
     void(b ? a : c);
     b ? a : C(); // expected-error {{deleted}}
-    b ? c : A(); // expected-error {{deleted}}
-    b ? A() : C(); // expected-error {{deleted}}
+    b ? c : A();
+#if __cplusplus <= 201402L
+    // expected-error at -2 {{deleted}}
+#endif
+    b ? A() : C();
+#if __cplusplus <= 201402L
+    // expected-error at -2 {{deleted}}
+#endif
   }
 }
 
@@ -874,10 +883,12 @@ namespace dr479 { // dr479: yes
   void f() {
     throw S();
     // expected-error at -1 {{temporary of type 'dr479::S' has private destructor}}
-    // expected-error at -2 {{calling a private constructor}}
-    // expected-error at -3 {{exception object of type 'dr479::S' has private destructor}}
+    // expected-error at -2 {{exception object of type 'dr479::S' has private destructor}}
 #if __cplusplus < 201103L
-    // expected-error at -5 {{C++98 requires an accessible copy constructor}}
+    // expected-error at -4 {{C++98 requires an accessible copy constructor}}
+#endif
+#if __cplusplus <= 201402L
+    // expected-error at -7 {{calling a private constructor}} (copy ctor)
 #endif
   }
   void g() {

Added: cfe/trunk/test/CodeGenCXX/cxx1z-copy-omission.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx1z-copy-omission.cpp?rev=288866&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cxx1z-copy-omission.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/cxx1z-copy-omission.cpp Tue Dec  6 17:52:28 2016
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -std=c++1z -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck %s
+
+struct A {
+  A(int);
+  A(A&&);
+  A(const A&);
+  ~A();
+
+  int arr[10];
+};
+
+A f();
+void h();
+
+// CHECK-LABEL: define {{.*}} @_Z1gv(
+void g() {
+  // CHECK: %[[A:.*]] = alloca
+  // CHECK-NOT: alloca
+  // CHECK-NOT: call
+  // CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]])
+  A a = A( A{ f() } );
+  // CHECK-NOT: call
+
+  // CHECK: call void @_Z1hv(
+  h();
+  // CHECK-NOT: call
+
+  // CHECK: call void @_ZN1AD1Ev({{.*}}* %[[A]])
+  // CHECK-NOT: call
+  // CHECK-LABEL: }
+}
+
+void f(A);
+
+// CHECK-LABEL: define {{.*}} @_Z1hv(
+void h() {
+  // CHECK: %[[A:.*]] = alloca
+  // CHECK-NOT: alloca
+  // CHECK-NOT: call
+
+  // CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]])
+  // CHECK-NOT: call
+  // CHECK: call {{.*}} @_Z1f1A({{.*}}* %[[A]])
+  f(f());
+  // CHECK-NOT: call
+  // CHECK: call void @_ZN1AD1Ev({{.*}}* %[[A]])
+
+  // CHECK: call void @_Z1hv(
+  h();
+
+  // CHECK-NOT: call
+  // CHECK-LABEL: }
+}
+
+// We still pass classes with trivial copy/move constructors and destructors in
+// registers, even if the copy is formally omitted.
+struct B {
+  B(int);
+  int n;
+};
+
+B fB();
+void fB(B);
+
+// CHECK-LABEL: define {{.*}} @_Z1iv(
+void i() {
+  // CHECK: %[[B:.*]] = alloca
+  // CHECK-NOT: alloca
+  // CHECK-NOT: call
+
+  // CHECK: %[[B_N:.*]] = call i32 @_Z2fBv()
+  // CHECK-NOT: call
+  // CHECK: store i32 %[[B_N]],
+  // CHECK-NOT: call
+  // CHECK: %[[B_N:.*]] = load i32
+  // CHECK-NOT: call
+  // CHECK: call void @_Z2fB1B(i32 %[[B_N]])
+  fB(fB());
+
+  // CHECK-LABEL: }
+}

Modified: cfe/trunk/test/SemaCXX/aggregate-initialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/aggregate-initialization.cpp?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/aggregate-initialization.cpp (original)
+++ cfe/trunk/test/SemaCXX/aggregate-initialization.cpp Tue Dec  6 17:52:28 2016
@@ -57,7 +57,7 @@ struct A {
   A(int);
   ~A();
   
-  A(const A&) = delete; // expected-note 2 {{'A' has been explicitly marked deleted here}}
+  A(const A&) = delete; // expected-note 0-2{{'A' has been explicitly marked deleted here}}
 };
 
 struct B {
@@ -70,10 +70,16 @@ struct C {
 
 void f() {
   A as1[1] = { };
-  A as2[1] = { 1 }; // expected-error {{copying array element of type 'A' invokes deleted constructor}}
+  A as2[1] = { 1 };
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{copying array element of type 'A' invokes deleted constructor}}
+#endif
 
   B b1 = { };
-  B b2 = { 1 }; // expected-error {{copying member subobject of type 'A' invokes deleted constructor}}
+  B b2 = { 1 };
+#if __cplusplus <= 201402L
+  // expected-error at -2 {{copying member subobject of type 'A' invokes deleted constructor}}
+#endif
   
   C c1 = { 1 };
 }

Added: cfe/trunk/test/SemaCXX/cxx1z-copy-omission.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1z-copy-omission.cpp?rev=288866&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1z-copy-omission.cpp (added)
+++ cfe/trunk/test/SemaCXX/cxx1z-copy-omission.cpp Tue Dec  6 17:52:28 2016
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -std=c++1z -verify %s
+
+struct Noncopyable {
+  Noncopyable();
+  Noncopyable(const Noncopyable &) = delete; // expected-note 1+{{deleted}}
+  virtual ~Noncopyable();
+};
+struct Derived : Noncopyable {};
+struct NoncopyableAggr {
+  Noncopyable nc;
+};
+struct Indestructible {
+  Indestructible();
+  ~Indestructible() = delete; // expected-note 1+{{deleted}}
+};
+struct Incomplete; // expected-note 1+{{declar}}
+
+Noncopyable make(int kind = 0) {
+  switch (kind) {
+  case 0: return {};
+  case 1: return Noncopyable();
+  case 2: return Noncopyable{};
+  case 3: return make();
+  }
+  __builtin_unreachable();
+}
+
+Indestructible make_indestructible();
+Incomplete make_incomplete(); // expected-note 1+{{here}}
+
+void take(Noncopyable nc) {}
+
+Noncopyable nrvo() {
+  Noncopyable nrvo;
+  return nrvo; // expected-error {{deleted constructor}}
+}
+
+Noncopyable nc1 = make();
+Noncopyable nc2 = Noncopyable();
+Noncopyable nc3 = Derived(); // expected-error {{deleted constructor}}
+
+NoncopyableAggr nca1 = NoncopyableAggr{};
+NoncopyableAggr nca2 = NoncopyableAggr{{}};
+NoncopyableAggr nca3 = NoncopyableAggr{NoncopyableAggr{Noncopyable()}};
+
+void test_expressions(bool b) {
+  auto lambda = [a = make()] {};
+
+  take({});
+  take(Noncopyable());
+  take(Noncopyable{});
+  take(make());
+
+  Noncopyable &&dc1 = dynamic_cast<Noncopyable&&>(Noncopyable());
+  Noncopyable &&dc2 = dynamic_cast<Noncopyable&&>(nc1);
+  Noncopyable &&dc3 = dynamic_cast<Noncopyable&&>(Derived());
+
+  Noncopyable sc1 = static_cast<Noncopyable>(Noncopyable());
+  Noncopyable sc2 = static_cast<Noncopyable>(nc1); // expected-error {{deleted}}
+  Noncopyable sc3 = static_cast<Noncopyable&&>(Noncopyable()); // expected-error {{deleted}}
+  Noncopyable sc4 = static_cast<Noncopyable>(static_cast<Noncopyable&&>(Noncopyable())); // expected-error {{deleted}}
+
+  Noncopyable cc1 = (Noncopyable)Noncopyable();
+  Noncopyable cc2 = (Noncopyable)Derived(); // expected-error {{deleted}}
+
+  Noncopyable fc1 = Noncopyable(Noncopyable());
+  Noncopyable fc2 = Noncopyable(Derived()); // expected-error {{deleted}}
+
+  // We must check for a complete type for every materialized temporary. (Note
+  // that in the special case of the top level of a decltype, no temporary is
+  // materialized.)
+  make_incomplete(); // expected-error {{incomplete}}
+  make_incomplete().a; // expected-error {{incomplete}}
+  make_incomplete().*(int Incomplete::*)nullptr; // expected-error {{incomplete}}
+  dynamic_cast<Incomplete&&>(make_incomplete()); // expected-error {{incomplete}}
+  const_cast<Incomplete&&>(make_incomplete()); // expected-error {{incomplete}}
+
+  sizeof(Indestructible{}); // expected-error {{deleted}}
+  sizeof(make_indestructible()); // expected-error {{deleted}}
+  sizeof(make_incomplete()); // expected-error {{incomplete}}
+  typeid(Indestructible{}); // expected-error {{deleted}}
+  typeid(make_indestructible()); // expected-error {{deleted}}
+  typeid(make_incomplete()); // expected-error {{incomplete}}
+
+  // FIXME: The first two cases here are now also valid in C++17 onwards.
+  using I = decltype(Indestructible()); // expected-error {{deleted}}
+  using I = decltype(Indestructible{}); // expected-error {{deleted}}
+  using I = decltype(make_indestructible());
+  using J = decltype(make_incomplete());
+
+  Noncopyable cond1 = b ? Noncopyable() : make();
+  Noncopyable cond2 = b ? Noncopyable() : Derived(); // expected-error {{incompatible}}
+  Noncopyable cond3 = b ? Derived() : Noncopyable(); // expected-error {{incompatible}}
+  Noncopyable cond4 = b ? Noncopyable() : nc1; // expected-error {{deleted}}
+  Noncopyable cond5 = b ? nc1 : Noncopyable(); // expected-error {{deleted}}
+  // Could convert both to an xvalue of type Noncopyable here, but we're not
+  // permitted to consider that.
+  Noncopyable &&cond6 = b ? Noncopyable() : Derived(); // expected-error {{incompatible}}
+  Noncopyable &&cond7 = b ? Derived() : Noncopyable(); // expected-error {{incompatible}}
+  // Could convert both to a const lvalue of type Noncopyable here, but we're
+  // not permitted to consider that, either.
+  const Noncopyable cnc;
+  const Noncopyable &cond8 = b ? cnc : Derived(); // expected-error {{incompatible}}
+  const Noncopyable &cond9 = b ? Derived() : cnc; // expected-error {{incompatible}}
+
+  extern const volatile Noncopyable make_cv();
+  Noncopyable cv_difference1 = make_cv();
+  const volatile Noncopyable cv_difference2 = make();
+}
+
+template<typename T> struct ConversionFunction { operator T(); };
+Noncopyable cf1 = ConversionFunction<Noncopyable>();
+Noncopyable cf2 = ConversionFunction<Noncopyable&&>(); // expected-error {{deleted}}
+Noncopyable cf3 = ConversionFunction<const volatile Noncopyable>();
+const volatile Noncopyable cf4 = ConversionFunction<Noncopyable>();
+Noncopyable cf5 = ConversionFunction<Derived>(); // expected-error {{deleted}}
+
+struct AsMember {
+  Noncopyable member;
+  AsMember() : member(make()) {}
+};
+// FIXME: DR (no number yet): we still get a copy for base or delegating construction.
+struct AsBase : Noncopyable {
+  AsBase() : Noncopyable(make()) {} // expected-error {{deleted}}
+};
+struct AsDelegating final {
+  AsDelegating(const AsDelegating &) = delete;
+  static AsDelegating make(int);
+
+  // The base constructor version of this is problematic; the complete object
+  // version would be OK. Perhaps we could allow copy omission here for final
+  // classes?
+  AsDelegating(int n) : AsDelegating(make(n)) {} // expected-error {{deleted}}
+};

Modified: cfe/trunk/www/cxx_dr_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/www/cxx_dr_status.html (original)
+++ cfe/trunk/www/cxx_dr_status.html Tue Dec  6 17:52:28 2016
@@ -6139,7 +6139,7 @@ and <I>POD class</I></td>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1054">1054</a></td>
     <td>C++11</td>
     <td>Lvalue-to-rvalue conversions in expression statements</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="none" align="center">No</td>
   </tr>
   <tr id="1055">
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1055">1055</a></td>

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=288866&r1=288865&r2=288866&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Tue Dec  6 17:52:28 2016
@@ -694,7 +694,7 @@ as the draft C++1z standard evolves.
     <tr>
       <td>Guaranteed copy elision</td>
       <td><a href="http://wg21.link/p0135r1">P0135R1</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td rowspan=2>Stricter expression evaluation order</td>




More information about the cfe-commits mailing list