[cfe-commits] r67059 - in /cfe/trunk: include/clang/AST/ include/clang/Basic/ lib/AST/ lib/CodeGen/ lib/Sema/ test/Parser/ test/SemaCXX/

Douglas Gregor dgregor at apple.com
Fri Mar 20 13:17:35 PDT 2009


Howard, there's a question for you below. Just search for your name.

Hi Sebastian,

On Mar 16, 2009, at 4:22 PM, Sebastian Redl wrote:

> Author: cornedbee
> Date: Mon Mar 16 18:22:08 2009
> New Revision: 67059
>
> URL: http://llvm.org/viewvc/llvm-project?rev=67059&view=rev
> Log:
> Almost complete implementation of rvalue references. One bug, and a  
> few unclear areas. Maybe Doug can shed some light on some of the  
> fixmes.

This is great; thanks! Comments interspersed.

> Modified: cfe/trunk/lib/AST/ASTContext.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/AST/ASTContext.cpp (original)
> +++ cfe/trunk/lib/AST/ASTContext.cpp Mon Mar 16 18:22:08 2009
> @@ -85,9 +85,9 @@
>   fprintf(stderr, "  %d types total.\n", (int)Types.size());
>   unsigned NumBuiltin = 0, NumPointer = 0, NumArray = 0,  
> NumFunctionP = 0;
>   unsigned NumVector = 0, NumComplex = 0, NumBlockPointer = 0;
> -  unsigned NumFunctionNP = 0, NumTypeName = 0, NumTagged = 0,  
> NumReference = 0;
> -  unsigned NumMemberPointer = 0;
> -
> +  unsigned NumFunctionNP = 0, NumTypeName = 0, NumTagged = 0;
> +  unsigned NumLValueReference = 0, NumRValueReference = 0,  
> NumMemberPointer = 0;
> +
>   unsigned NumTagStruct = 0, NumTagUnion = 0, NumTagEnum = 0,  
> NumTagClass = 0;
>   unsigned NumObjCInterfaces = 0, NumObjCQualifiedInterfaces = 0;
>   unsigned NumObjCQualifiedIds = 0;
> @@ -101,8 +101,10 @@
>       ++NumPointer;
>     else if (isa<BlockPointerType>(T))
>       ++NumBlockPointer;
> -    else if (isa<ReferenceType>(T))
> -      ++NumReference;
> +    else if (isa<LValueReferenceType>(T))
> +      ++NumLValueReference;
> +    else if (isa<RValueReferenceType>(T))
> +      ++NumRValueReference;

Icky. We should replace this with something that uses TypeNodes.def.

> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/AST/Expr.cpp (original)
> +++ cfe/trunk/lib/AST/Expr.cpp Mon Mar 16 18:22:08 2009
> @@ -575,10 +575,7 @@
>   if (TR->isVoidType() && !Ctx.getCanonicalType(TR).getCVRQualifiers 
> ())
>     return LV_IncompleteVoidType;
>
> -  /// FIXME: Expressions can't have reference type, so the following
> -  /// isn't needed.
> -  if (TR->isReferenceType()) // C++ [expr]
> -    return LV_Valid;
> +  assert(!TR->isReferenceType() && "Expressions can't have  
> reference type.");

Yay!

> Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon Mar 16 18:22:08 2009
> @@ -802,7 +802,7 @@
>     QualType ArgType = ClassType;
>     if (HasConstCopyConstructor)
>       ArgType = ArgType.withConst();
> -    ArgType = Context.getReferenceType(ArgType);
> +    ArgType = Context.getLValueReferenceType(ArgType);
>
>     //   An implicitly-declared copy constructor is an inline public
>     //   member of its class.
> @@ -880,10 +880,10 @@
>     //
>     //       X& X::operator=(X&)
>     QualType ArgType = ClassType;
> -    QualType RetType = Context.getReferenceType(ArgType);
> +    QualType RetType = Context.getLValueReferenceType(ArgType);
>     if (HasConstCopyAssignment)
>       ArgType = ArgType.withConst();
> -    ArgType = Context.getReferenceType(ArgType);
> +    ArgType = Context.getLValueReferenceType(ArgType);
>
>     //   An implicitly-declared copy assignment operator is an  
> inline public
>     //   member of its class.
> @@ -1630,7 +1630,8 @@
> Sema::ReferenceCompareResult
> Sema::CompareReferenceRelationship(QualType T1, QualType T2,
>                                    bool& DerivedToBase) {
> -  assert(!T1->isReferenceType() && "T1 must be the pointee type of  
> the reference type");
> +  assert(!T1->isReferenceType() &&
> +    "T1 must be the pointee type of the reference type");
>   assert(!T2->isReferenceType() && "T2 cannot be a reference type");
>
>   T1 = Context.getCanonicalType(T1);
> @@ -1713,6 +1714,7 @@
>   }
>
>   // Compute some basic properties of the types and the initializer.
> +  bool isRValRef = DeclType->isRValueReferenceType();
>   bool DerivedToBase = false;
>   Expr::isLvalueResult InitLvalue = Init->isLvalue(Context);
>   ReferenceCompareResult RefRelationship
> @@ -1738,6 +1740,15 @@
>       RefRelationship >= Ref_Compatible_With_Added_Qualification) {
>     BindsDirectly = true;
>
> +    // Rvalue references cannot bind to lvalues (N2812).
> +    // FIXME: This part of rvalue references is still in flux.  
> Revisit later.
> +    if (isRValRef) {
> +      if (!ICS)
> +        Diag(Init->getSourceRange().getBegin(),  
> diag::err_lvalue_to_rvalue_ref)
> +          << Init->getSourceRange();
> +      return true;
> +    }

Looks good! The FIXME can go away.

>     if (ICS) {
>       // C++ [over.ics.ref]p1:
>       //   When a parameter of reference type binds directly (8.5.3)
> @@ -1774,7 +1785,9 @@
>   //          92) (this conversion is selected by enumerating the
>   //          applicable conversion functions (13.3.1.6) and choosing
>   //          the best one through overload resolution (13.3)),
> -  if (!SuppressUserConversions && T2->isRecordType()) {
> +  // FIXME: Without standard language for N2812, the rvalue  
> reference treatment
> +  // here is pretty much a guess.
> +  if (!isRValRef && !SuppressUserConversions && T2->isRecordType()) {
>     // FIXME: Look for conversions in base classes!
>     CXXRecordDecl *T2RecordDecl
>       = dyn_cast<CXXRecordDecl>(T2->getAsRecordType()->getDecl());

Yup, good guess. The FIXME can go away.

> @@ -1790,7 +1803,7 @@
>       // If the conversion function doesn't return a reference type,
>       // it can't be considered for this conversion.
>       // FIXME: This will change when we support rvalue references.
> -      if (Conv->getConversionType()->isReferenceType() &&
> +      if (Conv->getConversionType()->isLValueReferenceType() &&
>           (AllowExplicit || !Conv->isExplicit()))
>         AddConversionCandidate(Conv, Init, DeclType, CandidateSet);
>     }

I believe that this is the right semantics, and you can remove the  
FIXME. However, I've never actually thought about this particular  
issue. However, do you agree that the following code should be ill- 
formed?

   struct A { };

   struct B {
     operator A&&();
   }

   void test(B b) {
     const A &ar = b; // error: calling operator A&&() returns an  
rvalue, which should not be considered
   }

I've added a test case to that effect.

> Modified: cfe/trunk/lib/Sema/SemaNamedCast.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaNamedCast.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/Sema/SemaNamedCast.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaNamedCast.cpp Mon Mar 16 18:22:08 2009
> @@ -115,9 +115,10 @@
>
>   DestType = Self.Context.getCanonicalType(DestType);
>   QualType SrcType = SrcExpr->getType();
> -  if (const ReferenceType *DestTypeTmp = DestType- 
> >getAsReferenceType()) {
> +  if (const LValueReferenceType *DestTypeTmp =
> +        DestType->getAsLValueReferenceType()) {
>     if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
> -      // Cannot cast non-lvalue to reference type.
> +      // Cannot cast non-lvalue to lvalue reference type.
>       Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
>         << "const_cast" << OrigDestType << SrcExpr->getSourceRange();
>       return;
> @@ -141,6 +142,8 @@
>   if (!DestType->isPointerType() && !DestType->isMemberPointerType 
> ()) {
>     // Cannot cast to non-pointer, non-reference type. Note that, if  
> DestType
>     // was a reference type, we converted it to a pointer above.
> +    // The status of rvalue references isn't entirely clear, but it  
> looks like
> +    // conversion to them is simply invalid.
>     // C++ 5.2.11p3: For two pointer types [...]
>     Self.Diag(OpRange.getBegin(), diag::err_bad_const_cast_dest)
>       << OrigDestType << DestRange;

Did you see [expr.const.cast]p4? It specifies that one can explicitly  
convert to an rvalue of type T2 using the cast const_cast<T2&&>. So,  
at the very least, we have to set DestType and SrcType appropriately  
when we're converting to an rvalue reference.

> @@ -214,7 +217,8 @@
>
>   DestType = Self.Context.getCanonicalType(DestType);
>   QualType SrcType = SrcExpr->getType();
> -  if (const ReferenceType *DestTypeTmp = DestType- 
> >getAsReferenceType()) {
> +  if (const LValueReferenceType *DestTypeTmp =
> +        DestType->getAsLValueReferenceType()) {
>     if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
>       // Cannot cast non-lvalue to reference type.
>       Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
> @@ -228,6 +232,14 @@
>     // This code does this transformation for the checked types.
>     DestType = Self.Context.getPointerType(DestTypeTmp- 
> >getPointeeType());
>     SrcType = Self.Context.getPointerType(SrcType);
> +  } else if (const RValueReferenceType *DestTypeTmp =
> +               DestType->getAsRValueReferenceType()) {
> +    // Both the reference conversion and the rvalue rules apply.
> +    Self.DefaultFunctionArrayConversion(SrcExpr);
> +    SrcType = SrcExpr->getType();
> +
> +    DestType = Self.Context.getPointerType(DestTypeTmp- 
> >getPointeeType());
> +    SrcType = Self.Context.getPointerType(SrcType);
>   } else {
>     // C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and
>     //   function-to-pointer standard conversions are performed on the
> @@ -425,6 +437,8 @@
>   // conversion using B's conversion constructor.
>   // DR 427 specifies that the downcast is to be applied here.
>
> +  // FIXME: With N2812, casts to rvalue refs will change.
> +

Right, and static_cast can be used to case an lvalue to an rvalue  
reference. That's the big change from the previous rvalue-references  
wording.

> Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Mar 16 18:22:08 2009
> @@ -519,8 +519,10 @@
>     // We were able to resolve the address of the overloaded function,
>     // so we can convert to the type of that function.
>     FromType = Fn->getType();
> -    if (ToType->isReferenceType())
> -      FromType = Context.getReferenceType(FromType);
> +    if (ToType->isLValueReferenceType())
> +      FromType = Context.getLValueReferenceType(FromType);
> +    else if (ToType->isRValueReferenceType())
> +      FromType = Context.getRValueReferenceType(FromType);
>     else if (ToType->isMemberPointerType()) {
>       // Resolve address only succeeds if both sides are member  
> pointers,
>       // but it doesn't have to be the same class. See DR 247.
> @@ -2734,7 +2736,7 @@
>          Arith < NumArithmeticTypes; ++Arith) {
>       QualType ArithTy = ArithmeticTypes[Arith];
>       QualType ParamTypes[2]
> -        = { Context.getReferenceType(ArithTy), Context.IntTy };
> +        = { Context.getLValueReferenceType(ArithTy), Context.IntTy };
>
>       // Non-volatile version.
>       if (NumArgs == 1)
> @@ -2743,7 +2745,7 @@
>         AddBuiltinCandidate(ArithTy, ParamTypes, Args, 2,  
> CandidateSet);
>
>       // Volatile version
> -      ParamTypes[0] = Context.getReferenceType(ArithTy.withVolatile 
> ());
> +      ParamTypes[0] = Context.getLValueReferenceType 
> (ArithTy.withVolatile());
>       if (NumArgs == 1)
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1,  
> CandidateSet);
>       else
> @@ -2767,7 +2769,7 @@
>         continue;
>
>       QualType ParamTypes[2] = {
> -        Context.getReferenceType(*Ptr), Context.IntTy
> +        Context.getLValueReferenceType(*Ptr), Context.IntTy
>       };
>
>       // Without volatile
> @@ -2778,7 +2780,7 @@
>
>       if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) {
>         // With volatile
> -        ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile 
> ());
> +        ParamTypes[0] = Context.getLValueReferenceType 
> ((*Ptr).withVolatile());
>         if (NumArgs == 1)
>           AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1,  
> CandidateSet);
>         else
> @@ -2802,7 +2804,7 @@
>          Ptr != CandidateTypes.pointer_end(); ++Ptr) {
>       QualType ParamTy = *Ptr;
>       QualType PointeeTy = ParamTy->getAsPointerType()- 
> >getPointeeType();
> -      AddBuiltinCandidate(Context.getReferenceType(PointeeTy),
> +      AddBuiltinCandidate(Context.getLValueReferenceType(PointeeTy),
>                           &ParamTy, Args, 1, CandidateSet);
>     }
>     break;
> @@ -3021,14 +3023,14 @@
>       QualType ParamTypes[2];
>
>       // T& operator=(T&, T)
> -      ParamTypes[0] = Context.getReferenceType(*Enum);
> +      ParamTypes[0] = Context.getLValueReferenceType(*Enum);
>       ParamTypes[1] = *Enum;
>       AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                           /*IsAssignmentOperator=*/false);
>
>       if (!Context.getCanonicalType(*Enum).isVolatileQualified()) {
>         // volatile T& operator=(volatile T&, T)
> -        ParamTypes[0] = Context.getReferenceType 
> ((*Enum).withVolatile());
> +        ParamTypes[0] = Context.getLValueReferenceType 
> ((*Enum).withVolatile());
>         ParamTypes[1] = *Enum;
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                             /*IsAssignmentOperator=*/false);
> @@ -3060,13 +3062,13 @@
>       ParamTypes[1] = (Op == OO_Equal)? *Ptr :  
> Context.getPointerDiffType();
>
>       // non-volatile version
> -      ParamTypes[0] = Context.getReferenceType(*Ptr);
> +      ParamTypes[0] = Context.getLValueReferenceType(*Ptr);
>       AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                           /*IsAssigmentOperator=*/Op == OO_Equal);
>
>       if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) {
>         // volatile version
> -        ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile 
> ());
> +        ParamTypes[0] = Context.getLValueReferenceType 
> ((*Ptr).withVolatile());
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                             /*IsAssigmentOperator=*/Op == OO_Equal);
>       }
> @@ -3094,13 +3096,13 @@
>         ParamTypes[1] = ArithmeticTypes[Right];
>
>         // Add this built-in operator as a candidate (VQ is empty).
> -        ParamTypes[0] = Context.getReferenceType(ArithmeticTypes 
> [Left]);
> +        ParamTypes[0] = Context.getLValueReferenceType 
> (ArithmeticTypes[Left]);
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                             /*IsAssigmentOperator=*/Op == OO_Equal);
>
>         // Add this built-in operator as a candidate (VQ is  
> 'volatile').
>         ParamTypes[0] = ArithmeticTypes[Left].withVolatile();
> -        ParamTypes[0] = Context.getReferenceType(ParamTypes[0]);
> +        ParamTypes[0] = Context.getLValueReferenceType(ParamTypes 
> [0]);
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet,
>                             /*IsAssigmentOperator=*/Op == OO_Equal);
>       }
> @@ -3132,13 +3134,13 @@
>         ParamTypes[1] = ArithmeticTypes[Right];
>
>         // Add this built-in operator as a candidate (VQ is empty).
> -        ParamTypes[0] = Context.getReferenceType(ArithmeticTypes 
> [Left]);
> +        ParamTypes[0] = Context.getLValueReferenceType 
> (ArithmeticTypes[Left]);
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet);
>
>         // Add this built-in operator as a candidate (VQ is  
> 'volatile').
>         ParamTypes[0] = ArithmeticTypes[Left];
>         ParamTypes[0].addVolatile();
> -        ParamTypes[0] = Context.getReferenceType(ParamTypes[0]);
> +        ParamTypes[0] = Context.getLValueReferenceType(ParamTypes 
> [0]);
>         AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2,  
> CandidateSet);
>       }
>     }
> @@ -3190,7 +3192,7 @@
>          Ptr != CandidateTypes.pointer_end(); ++Ptr) {
>       QualType ParamTypes[2] = { *Ptr, Context.getPointerDiffType() };
>       QualType PointeeType = (*Ptr)->getAsPointerType()- 
> >getPointeeType();
> -      QualType ResultTy = Context.getReferenceType(PointeeType);
> +      QualType ResultTy = Context.getLValueReferenceType 
> (PointeeType);
>
>       // T& operator[](T*, ptrdiff_t)
>       AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2,  
> CandidateSet);
> @@ -3407,11 +3409,17 @@
>         // retaining as many typedefs as possible while still showing
>         // the function type (and, therefore, its parameter types).
>         QualType FnType = Cand->Surrogate->getConversionType();
> -        bool isReference = false;
> +        bool isLValueReference = false;
> +        bool isRValueReference = false;
>         bool isPointer = false;
> -        if (const ReferenceType *FnTypeRef = FnType- 
> >getAsReferenceType()) {
> +        if (const LValueReferenceType *FnTypeRef =
> +              FnType->getAsLValueReferenceType()) {
>           FnType = FnTypeRef->getPointeeType();
> -          isReference = true;
> +          isLValueReference = true;
> +        } else if (const RValueReferenceType *FnTypeRef =
> +                     FnType->getAsRValueReferenceType()) {
> +          FnType = FnTypeRef->getPointeeType();
> +          isRValueReference = true;
>         }
>         if (const PointerType *FnTypePtr = FnType->getAsPointerType 
> ()) {
>           FnType = FnTypePtr->getPointeeType();
> @@ -3421,7 +3429,8 @@
>         FnType = QualType(FnType->getAsFunctionType(), 0);
>         // Reconstruct the pointer/reference as appropriate.
>         if (isPointer) FnType = Context.getPointerType(FnType);
> -        if (isReference) FnType = Context.getReferenceType(FnType);
> +        if (isRValueReference) FnType =  
> Context.getRValueReferenceType(FnType);
> +        if (isLValueReference) FnType =  
> Context.getLValueReferenceType(FnType);
>
>         Diag(Cand->Surrogate->getLocation(),  
> diag::err_ovl_surrogate_cand)
>           << FnType;
> @@ -4144,7 +4153,7 @@
>     // FIXME: Represent the user-defined conversion in the AST!
>     ImpCastExprToType(Object,
>                       Conv->getConversionType().getNonReferenceType(),
> -                      Conv->getConversionType()->isReferenceType());
> +                      Conv->getConversionType()- 
> >isLValueReferenceType());
>     return ActOnCallExpr(S, ExprArg(*this, Object), LParenLoc,
>                          MultiExprArg(*this, (ExprTy**)Args, NumArgs),
>                          CommaLocs, RParenLoc).release();

It looks like you haven't implemented the implicit conversion ranking  
logic for rvalue reference bindings. which would go near the end of  
Sema::CompareStandardConversionSequences. This logic should be able to  
choose appropriately between binding to "const X&" and binding to  
"X&&" for rvalues. I've checked in a commented-out, FIXME'd test case  
in rval-references.cpp that's currently not working.

> Modified: cfe/trunk/lib/Sema/SemaType.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/lib/Sema/SemaType.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaType.cpp Mon Mar 16 18:22:08 2009
> @@ -319,8 +319,16 @@
> ///
> /// \returns A suitable reference type, if there are no
> /// errors. Otherwise, returns a NULL type.
> -QualType Sema::BuildReferenceType(QualType T, unsigned Quals,
> +QualType Sema::BuildReferenceType(QualType T, bool LValueRef,  
> unsigned Quals,
>                                   SourceLocation Loc,  
> DeclarationName Entity) {
> +  if (LValueRef) {
> +    if (const RValueReferenceType *R = T->getAsRValueReferenceType 
> ()) {
> +      // FIXME: Find the C++0x reference for reference collapsing.
> +      // In reference collapsing, lvalue refs win over rvalue refs.
> +      return Context.getLValueReferenceType(R->getPointeeType()).
> +               getQualifiedType(Quals);
> +    }
> +  }

Okay, this is reference collapsing for && & -> &. Per my former  
comment about dropping the new qualifiers (Quals), the right return  
would be:

   return Context.getLvalueReferenceType(R->getPointerType 
()).getQualifiedType(T.getCVRQualifiers());

That preserves the qualifiers that T already had (which is either  
'restrict' or no qualifiers), but converts the rvalue reference type  
into an lvalue reference type.

> Added: cfe/trunk/test/SemaCXX/rval-references.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/rval-references.cpp?rev=67059&view=auto
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/test/SemaCXX/rval-references.cpp (added)
> +++ cfe/trunk/test/SemaCXX/rval-references.cpp Mon Mar 16 18:22:08  
> 2009
> @@ -0,0 +1,33 @@
> +// RUN: clang -fsyntax-only -verify -std=c++0x %s
> +
> +typedef int&& irr;
> +typedef irr& ilr_c1; // Collapses to int&
> +typedef int& ilr;
> +typedef ilr&& ilr_c2; // Collapses to int&
> +
> +irr ret_irr() {
> +  return 0;
> +}
> +
> +struct not_int {};
> +
> +int over(int&);
> +not_int over(int&&);
> +
> +void f() {
> +  int &&virr1; // expected-error {{declaration of reference  
> variable 'virr1' requires an initializer}}
> +  int &&virr2 = 0;
> +  // FIXME: named rvalue references are lvalues!
> +  //int &&virr3 = virr1; // xpected-error {{rvalue reference cannot  
> bind to lvalue}}

It turns out that this behavior works fine, but since virr1 was marked  
invalid (due to the missing initialization), we weren't performing the  
type-check. Yay!

> +  int i1 = 0;
> +  int &&virr4 = i1; // expected-error {{rvalue reference cannot  
> bind to lvalue}}
> +  int &&virr5 = ret_irr();
> +
> +  int i2 = over(i1);
> +  not_int ni1 = over(0);
> +  int i3 = over(virr2);
> +  not_int ni2 = over(ret_irr());
> +
> +  ilr_c1 vilr1 = i1;
> +  ilr_c2 vilr2 = i1;
> +}

I accidentally ran the rval-references test without -std=c++0x, and  
the error messages are a bit scary. I suggest that we parse the rvalue  
reference regardless of C++ dialect but give a "rvalue references are  
a C++0x extension" warning in C++03.

> Modified: cfe/trunk/test/SemaCXX/static-cast.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/static-cast.cpp?rev=67059&r1=67058&r2=67059&view=diff
>
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> = 
> ======================================================================
> --- cfe/trunk/test/SemaCXX/static-cast.cpp (original)
> +++ cfe/trunk/test/SemaCXX/static-cast.cpp Mon Mar 16 18:22:08 2009
> @@ -54,7 +54,7 @@
>   //(void)static_cast<A*>((H*)0); // {{static_cast from 'struct H *'  
> to 'struct A *' is not allowed}}
>   (void)static_cast<int>((int*)0); // expected-error {{static_cast  
> from 'int *' to 'int' is not allowed}}
>   (void)static_cast<A**>((B**)0); // expected-error {{static_cast  
> from 'struct B **' to 'struct A **' is not allowed}}
> -  (void)static_cast<char&>(i); // expected-error {{non-const  
> reference to type 'char' cannot be initialized with a value of type  
> 'int'}}
> +  (void)static_cast<char&>(i); // expected-error {{non-const lvalue  
> reference to type 'char' cannot be initialized with a value of type  
> 'int'}}
> }
>
> // Anything to void
> @@ -86,7 +86,7 @@
>   (void)static_cast<H*>((A*)0); // expected-error {{ambiguous  
> static_cast from base 'struct A' to derived 'struct H':\n    struct  
> A -> struct B -> struct G1 -> struct H\n    struct A -> struct B ->  
> struct G2 -> struct H}}
>   (void)static_cast<H&>(*((A*)0)); // expected-error {{ambiguous  
> static_cast from base 'struct A' to derived 'struct H':\n    struct  
> A -> struct B -> struct G1 -> struct H\n    struct A -> struct B ->  
> struct G2 -> struct H}}
>   (void)static_cast<E*>((B*)0); // expected-error {{static_cast from  
> 'struct B *' to 'struct E *' is not allowed}}
> -  (void)static_cast<E&>(*((B*)0)); // expected-error {{non-const  
> reference to type 'struct E' cannot be initialized with a value of  
> type 'struct B'}}
> +  (void)static_cast<E&>(*((B*)0)); // expected-error {{non-const  
> lvalue reference to type 'struct E' cannot be initialized with a  
> value of type 'struct B'}}

We definitely need tests for static_casting to an rvalue reference type.

What you've implemented looks good. There's a bit more work to do to  
finish this off. For example, when returning a non-static local from a  
function, we need to do the "try to return it as an rvalue; if that  
doesn't work, try it as an lvalue" dance. To actually test this, we'll  
need some way to detect whether we're getting the right  
constructors... since we don't have access control or code-generation  
yet, the easiest way forward might be to put in the syntax for ' =  
delete'. Most of the functionality is already there.

On the testing front, we should import Howard's rvalue-reference tests  
to make sure we've covered everything that we can do now. We obviously  
can't implement the template-argument deduction bits, but we can test  
reference-collapsing in template instantiations, and most everything  
else.

Thanks for working on this!

   - Doug



More information about the cfe-commits mailing list