Hmm, never mind. The test seems to preclude copy elision if I'm looking in the right place:
std::streambuf &get(); int main() { std::streambuf sb = get(); // expected-error } > On 2016-Dec-08, at 19:17, Duncan P. N. Exon Smith via cfe-commits > <cfe-commits@lists.llvm.org> wrote: > > +Eric, Marshall > > I haven't looked, but: from the test name, "copy.fail.cpp", I suspect there > is a bug/incompatibility in the test. It likely relies on the compiler > trying (and failing) to copy something in a context where r288866 guarantees > that there is no copy. > >> On 2016-Dec-08, at 18:00, Adrian Prantl <apra...@apple.com> wrote: >> >> Hi Richard, >> >> at this point this is more a heads-up than anything actionable, but I wanted >> to let you know that I bisected this bot failure >> (http://lab.llvm.org:8080/green/job/clang-stage2-cmake-RgSan_check/2721/consoleFull#10584592348254eaf0-7326-4999-85b0-388101f2d404) >> of std/input.output/stream.buffers/streambuf/streambuf.cons/copy.fail.cpp >> down to this commit. >> >> I will follow up once I have a better understanding what is going on there. >> >> -- adrian >> >>> On Dec 6, 2016, at 3:52 PM, Richard Smith via cfe-commits >>> <cfe-commits@lists.llvm.org> wrote: >>> >>> 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@-2 {{no matching constructor}} expected-note@-2 >>> {{candidate}} expected-note@-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@-3 2{{volatile qualifier}} >>> - // expected-note@-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@-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@-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@-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@-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@-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@-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@-2 {{deleted}} >>> +#endif >>> + b ? A() : C(); >>> +#if __cplusplus <= 201402L >>> + // expected-error@-2 {{deleted}} >>> +#endif >>> } >>> } >>> >>> @@ -874,10 +883,12 @@ namespace dr479 { // dr479: yes >>> void f() { >>> throw S(); >>> // expected-error@-1 {{temporary of type 'dr479::S' has private >>> destructor}} >>> - // expected-error@-2 {{calling a private constructor}} >>> - // expected-error@-3 {{exception object of type 'dr479::S' has private >>> destructor}} >>> + // expected-error@-2 {{exception object of type 'dr479::S' has private >>> destructor}} >>> #if __cplusplus < 201103L >>> - // expected-error@-5 {{C++98 requires an accessible copy constructor}} >>> + // expected-error@-4 {{C++98 requires an accessible copy constructor}} >>> +#endif >>> +#if __cplusplus <= 201402L >>> + // expected-error@-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@-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@-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> >>> >>> >>> _______________________________________________ >>> cfe-commits mailing list >>> cfe-commits@lists.llvm.org >>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >> > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits