]> granicus.if.org Git - clang/commitdiff
Implement initialization of a reference (C++ [dcl.init.ref]) as part
authorDouglas Gregor <dgregor@apple.com>
Wed, 29 Oct 2008 00:13:59 +0000 (00:13 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 29 Oct 2008 00:13:59 +0000 (00:13 +0000)
of copy initialization. Other pieces of the puzzle:

  - Try/Perform-ImplicitConversion now handles implicit conversions
    that don't involve references.
  - Try/Perform-CopyInitialization uses
    CheckSingleAssignmentConstraints for C. PerformCopyInitialization
    is now used for all argument passing and returning values from a
    function.
  - Diagnose errors with declaring references and const values without
    an initializer. (Uses a new Action callback, ActOnUninitializedDecl).

We do not yet have implicit conversion sequences for reference
binding, which means that we don't have any overloading support for
reference parameters yet.

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

17 files changed:
include/clang/AST/Expr.h
include/clang/Basic/DiagnosticKinds.def
include/clang/Parse/Action.h
lib/AST/Expr.cpp
lib/Parse/ParseDecl.cpp
lib/Sema/Sema.h
lib/Sema/SemaChecking.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaInherit.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaStmt.cpp
test/Parser/cxx-reference.cpp
test/SemaCXX/overload-call.cpp
test/SemaCXX/references.cpp

index 20f51761dc7fc85567540add1a967af060b21eea..d6aa5bfc548b3d1bf7e14f046cc9f0e5f6c5d53a 100644 (file)
@@ -105,6 +105,7 @@ public:
   isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const;
   
   bool isNullPointerConstant(ASTContext &Ctx) const;
+  bool isBitField();
 
   /// getIntegerConstantExprValue() - Return the value of an integer
   /// constant expression. The expression must be a valid integer
@@ -153,7 +154,7 @@ public:
   Expr* IgnoreParens();
 
   /// IgnoreParenCasts - Ignore parentheses and casts.  Strip off any ParenExpr
-  /// or CastExprs or ImplicitCastExprs, returning their operand.
+  /// or CastExprs, returning their operand.
   Expr *IgnoreParenCasts();
   
   const Expr* IgnoreParens() const {
index 9f59f947c690d7bdd4383788d2c3ac89be78b4dc..877452dc916291752dbaa7ca866ee2094ae70ade 100644 (file)
@@ -676,6 +676,16 @@ DIAG(err_not_integral_type_bitfield, ERROR,
 DIAG(err_member_initialization, ERROR,
     "'%0' can only be initialized if it is a static const integral data member")
 
+// C++ initialization
+DIAG(err_not_reference_to_const_init, ERROR,
+     "non-const reference to type '%0' cannot be initialized with a %1 of type '%2'")
+DIAG(err_reference_init_drops_quals, ERROR,
+     "initialization of reference to type '%0' with a %1 of type '%2' drops qualifiers")
+DIAG(err_reference_var_requires_init, ERROR,
+     "declaration of reference variable '%0' requires an initializer")
+DIAG(err_const_var_requires_init, ERROR,
+     "declaration of const variable '%0' requires an initializer")
+
 // Attributes
 DIAG(err_attribute_wrong_number_arguments, ERROR,
      "attribute requires %0 argument(s)")
index 8c414e49aed8b4b3bfc56e495fa6c68039899276..7564aefdc87cfcbab63cd7bb2a60f224f47c5012 100644 (file)
@@ -130,6 +130,13 @@ public:
   virtual void AddInitializerToDecl(DeclTy *Dcl, ExprTy *Init) {
     return;
   }
+
+  /// ActOnUninitializedDecl - This action is called immediately after
+  /// ActOnDeclarator (when an initializer is *not* present).
+  virtual void ActOnUninitializedDecl(DeclTy *Dcl) {
+    return;
+  }
+
   /// FinalizeDeclaratorGroup - After a sequence of declarators are parsed, this
   /// gives the actions implementation a chance to process the group as a whole.
   virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group) {
index 30e06a6de54cd5a7fbf9a0d148946133687d45f6..44d6ca1012dc12b046151fbd11a05a735f17fd60 100644 (file)
@@ -397,8 +397,7 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
     // C++ [expr.call]p10:
     //   A function call is an lvalue if and only if the result type
     //   is a reference.
-    QualType CalleeType 
-      = cast<CallExpr>(this)->getCallee()->IgnoreParens()->getType();
+    QualType CalleeType = cast<CallExpr>(this)->getCallee()->getType();
     if (const PointerType *FnTypePtr = CalleeType->getAsPointerType())
       if (const FunctionType *FnType
             = FnTypePtr->getPointeeType()->getAsFunctionType())
@@ -1106,6 +1105,14 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx) const {
   return isIntegerConstantExpr(Val, Ctx, 0, true) && Val == 0;
 }
 
+/// isBitField - Return true if this expression is a bit-field.
+bool Expr::isBitField() {
+  Expr *E = this->IgnoreParenCasts();
+  if (MemberExpr *MemRef = dyn_cast<MemberExpr>(E))
+    return MemRef->getMemberDecl()->isBitField();
+  return false;
+}
+
 unsigned ExtVectorElementExpr::getNumElements() const {
   if (const VectorType *VT = getType()->getAsVectorType())
     return VT->getNumElements();
index 6bcb90b8ae659ef69924d176ff066bf00f03e761..243a15f3dbd6b25f7c86718cf28436712708e2ce 100644 (file)
@@ -310,6 +310,8 @@ ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) {
                                               &Exprs[0], Exprs.size(),
                                               &CommaLocs[0], RParenLoc);
       }
+    } else {
+      Actions.ActOnUninitializedDecl(LastDeclInGroup);
     }
     
     // If we don't have a comma, it is either the end of the list (a ';') or an
index f84b5c5e712e102380174f0c42621e95bb13729c..87d7cd139f1737b84c6cfccd80216e50e6beb3e9 100644 (file)
@@ -279,6 +279,7 @@ private:
                                          SourceLocation EqualLoc,
                                          ExprTy *defarg);
   void AddInitializerToDecl(DeclTy *dcl, ExprTy *init);
+  void ActOnUninitializedDecl(DeclTy *dcl);
   virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group);
 
   virtual DeclTy *ActOnStartOfFunctionDef(Scope *S, Declarator &D);
@@ -369,7 +370,7 @@ private:
   /// C++ Overloading.
   bool IsOverload(FunctionDecl *New, Decl* OldD, 
                   OverloadedFunctionDecl::function_iterator &MatchedDecl);
-  ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType);
+  ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType);
   bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType);
   bool IsFloatingPointPromotion(QualType FromType, QualType ToType);
   bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
@@ -393,6 +394,10 @@ private:
   CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
                                   const StandardConversionSequence& SCS2);
 
+  ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType);
+  bool PerformCopyInitialization(Expr *&From, QualType ToType, 
+                                 const char *Flavor);
+
   /// OverloadingResult - Capture the result of performing overload
   /// resolution.
   enum OverloadingResult {
@@ -1111,6 +1116,34 @@ private:
   StringLiteral *IsStringLiteralInit(Expr *Init, QualType DeclType);
   bool CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT);
 
+  // type checking C++ declaration initializers (C++ [dcl.init]).
+
+  /// ReferenceCompareResult - Expresses the result of comparing two
+  /// types (cv1 T1 and cv2 T2) to determine their compatibility for the
+  /// purposes of initialization by reference (C++ [dcl.init.ref]p4).
+  enum ReferenceCompareResult {
+    /// Ref_Incompatible - The two types are incompatible, so direct
+    /// reference binding is not possible.
+    Ref_Incompatible = 0,
+    /// Ref_Related - The two types are reference-related, which means
+    /// that their unqualified forms (T1 and T2) are either the same
+    /// or T1 is a base class of T2.
+    Ref_Related,
+    /// Ref_Compatible_With_Added_Qualification - The two types are
+    /// reference-compatible with added qualification, meaning that
+    /// they are reference-compatible and the qualifiers on T1 (cv1)
+    /// are greater than the qualifiers on T2 (cv2).
+    Ref_Compatible_With_Added_Qualification,
+    /// Ref_Compatible - The two types are reference-compatible and
+    /// have equivalent qualifiers (cv1 == cv2).
+    Ref_Compatible
+  };
+
+  ReferenceCompareResult CompareReferenceRelationship(QualType T1, QualType T2);
+
+  bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType,
+                          bool Complain = true);
+
   /// CheckCastTypes - Check type constraints for casting between types.
   bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr);
   
index ac41bfc431d785590d27e8c68a585bd588290087..816d6fd05d60bd672c6ec2f934f926a8130f95a5 100644 (file)
@@ -886,7 +886,7 @@ static DeclRefExpr* EvalVal(Expr *E) {
     DeclRefExpr *DR = cast<DeclRefExpr>(E);
       
     if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl()))
-      if(V->hasLocalStorage()) return DR;
+      if(V->hasLocalStorage() && !V->getType()->isReferenceType()) return DR;
       
     return NULL;
   }
index 71c0ceb57fb927017e608960034ba6155264f58f..78f0d8f8be66309d7775603a389089d1d224b7d8 100644 (file)
@@ -628,6 +628,13 @@ StringLiteral *Sema::IsStringLiteralInit(Expr *Init, QualType DeclType) {
 }
 
 bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType) {  
+  // C++ [dcl.init.ref]p1:
+  //   A variable declared to be a T&, that is “reference to type T”
+  //   (8.3.2), shall be initialized by an object, or function, of
+  //   type T or by an object that can be converted into a T.
+  if (DeclType->isReferenceType())
+    return CheckReferenceInit(Init, DeclType);
+
   // C99 6.7.8p3: The type of the entity to be initialized shall be an array
   // of unknown size ("[]") or an object type that is not a variable array type.
   if (const VariableArrayType *VAT = Context.getAsVariableArrayType(DeclType))
@@ -1516,6 +1523,49 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprTy *init) {
   return;
 }
 
+void Sema::ActOnUninitializedDecl(DeclTy *dcl) {
+  Decl *RealDecl = static_cast<Decl *>(dcl);
+
+  if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
+    QualType Type = Var->getType();
+    // C++ [dcl.init.ref]p3:
+    //   The initializer can be omitted for a reference only in a
+    //   parameter declaration (8.3.5), in the declaration of a
+    //   function return type, in the declaration of a class member
+    //   within its class declaration (9.2), and where the extern
+    //   specifier is explicitly used.
+    if (Type->isReferenceType() && Var->getStorageClass() != VarDecl::Extern)
+      Diag(Var->getLocation(),
+           diag::err_reference_var_requires_init,
+           Var->getName(), 
+           SourceRange(Var->getLocation(), Var->getLocation()));
+
+    // C++ [dcl.init]p9:
+    //
+    //     If no initializer is specified for an object, and the
+    //     object is of (possibly cv-qualified) non-POD class type (or
+    //     array thereof), the object shall be default-initialized; if
+    //     the object is of const-qualified type, the underlying class
+    //     type shall have a user-declared default
+    //     constructor. Otherwise, if no initializer is specified for
+    //     an object, the object and its subobjects, if any, have an
+    //     indeterminate initial value; if the object or any of its
+    //     subobjects are of const-qualified type, the program is
+    //     ill-formed.
+    //
+    // This isn't technically an error in C, so we don't diagnose it.
+    //
+    // FIXME: Actually perform the POD/user-defined default
+    // constructor check.
+    if (getLangOptions().CPlusPlus &&
+             Context.getCanonicalType(Type).isConstQualified())
+      Diag(Var->getLocation(), 
+           diag::err_const_var_requires_init, 
+           Var->getName(), 
+           SourceRange(Var->getLocation(), Var->getLocation()));
+  }
+}
+
 /// The declarators are chained together backwards, reverse the list.
 Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) {
   // Often we have single declarators, handle them quickly.
index 729bd7c35ccbfdbce568cd8322ca7275914712f9..7e35ff4df3a7a15c6806c6d59d35fd92f6592ee8 100644 (file)
@@ -679,3 +679,193 @@ void Sema::AddCXXDirectInitializerToDecl(DeclTy *Dcl, SourceLocation LParenLoc,
   // Set the init expression, handles conversions.
   AddInitializerToDecl(Dcl, ExprTys[0]);
 }
+
+/// CompareReferenceRelationship - Compare the two types T1 and T2 to
+/// determine whether they are reference-related,
+/// reference-compatible, reference-compatible with added
+/// qualification, or incompatible, for use in C++ initialization by
+/// reference (C++ [dcl.ref.init]p4). Neither type can be a reference
+/// type, and the first type (T1) is the pointee type of the reference
+/// type being initialized.
+Sema::ReferenceCompareResult 
+Sema::CompareReferenceRelationship(QualType T1, QualType T2) {
+  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);
+  T2 = Context.getCanonicalType(T2);
+  QualType UnqualT1 = T1.getUnqualifiedType();
+  QualType UnqualT2 = T2.getUnqualifiedType();
+
+  // C++ [dcl.init.ref]p4:
+  //   Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is
+  //   reference-related to “cv2 T2” if T1 is the same type as T2, or 
+  //   T1 is a base class of T2.
+  //
+  // If neither of these conditions is met, the two types are not
+  // reference related at all.
+  if (UnqualT1 != UnqualT2 && !IsDerivedFrom(UnqualT2, UnqualT1))
+    return Ref_Incompatible;
+
+  // At this point, we know that T1 and T2 are reference-related (at
+  // least).
+
+  // C++ [dcl.init.ref]p4:
+  //   "cv1 T1” is reference-compatible with “cv2 T2” if T1 is
+  //   reference-related to T2 and cv1 is the same cv-qualification
+  //   as, or greater cv-qualification than, cv2. For purposes of
+  //   overload resolution, cases for which cv1 is greater
+  //   cv-qualification than cv2 are identified as
+  //   reference-compatible with added qualification (see 13.3.3.2).
+  if (T1.getCVRQualifiers() == T2.getCVRQualifiers())
+    return Ref_Compatible;
+  else if (T1.isMoreQualifiedThan(T2))
+    return Ref_Compatible_With_Added_Qualification;
+  else
+    return Ref_Related;
+}
+
+/// CheckReferenceInit - Check the initialization of a reference
+/// variable with the given initializer (C++ [dcl.init.ref]). Init is
+/// the initializer (either a simple initializer or an initializer
+/// list), and DeclType is the type of the declaration. When Complain
+/// is true, this routine will produce diagnostics (and return true)
+/// when the declaration cannot be initialized with the given
+/// initializer. When Complain is false, this routine will return true
+/// when the initialization cannot be performed, but will not produce
+/// any diagnostics or alter Init.
+bool Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, bool Complain) {
+  assert(DeclType->isReferenceType() && "Reference init needs a reference");
+
+  QualType T1 = DeclType->getAsReferenceType()->getPointeeType();
+  QualType T2 = Init->getType();
+
+  Expr::isLvalueResult InitLvalue = Init->isLvalue(Context);
+  ReferenceCompareResult RefRelationship = CompareReferenceRelationship(T1, T2);
+
+  // C++ [dcl.init.ref]p5:
+  //   A reference to type “cv1 T1” is initialized by an expression
+  //   of type “cv2 T2” as follows:
+
+  //     -- If the initializer expression
+
+  bool BindsDirectly = false;
+  //       -- is an lvalue (but is not a bit-field), and “cv1 T1” is
+  //          reference-compatible with “cv2 T2,” or
+  if (InitLvalue == Expr::LV_Valid && !Init->isBitField() &&
+      RefRelationship >= Ref_Compatible) {
+    BindsDirectly = true;
+
+    if (!Complain) {
+      // FIXME: Binding to a subobject of the lvalue is going to require
+      // more AST annotation than this.
+      ImpCastExprToType(Init, T1);    
+    }
+  }
+
+  //       -- has a class type (i.e., T2 is a class type) and can be
+  //          implicitly converted to an lvalue of type “cv3 T3,”
+  //          where “cv1 T1” is reference-compatible with “cv3 T3”
+  //          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)),
+  // FIXME: Implement this second bullet, once we have conversion
+  //        functions.
+
+  if (BindsDirectly) {
+    // C++ [dcl.init.ref]p4:
+    //   [...] In all cases where the reference-related or
+    //   reference-compatible relationship of two types is used to
+    //   establish the validity of a reference binding, and T1 is a
+    //   base class of T2, a program that necessitates such a binding
+    //   is ill-formed if T1 is an inaccessible (clause 11) or
+    //   ambiguous (10.2) base class of T2.
+    //
+    // Note that we only check this condition when we're allowed to
+    // complain about errors, because we should not be checking for
+    // ambiguity (or inaccessibility) unless the reference binding
+    // actually happens.
+    if (Complain && 
+        (Context.getCanonicalType(T1).getUnqualifiedType() 
+           != Context.getCanonicalType(T2).getUnqualifiedType()) && 
+        CheckDerivedToBaseConversion(T2, T1, Init->getSourceRange().getBegin(),
+                                     Init->getSourceRange()))
+      return true;
+          
+    return false;
+  }
+
+  //     -- Otherwise, the reference shall be to a non-volatile const
+  //        type (i.e., cv1 shall be const).
+  if (T1.getCVRQualifiers() != QualType::Const) {
+    if (Complain)
+      Diag(Init->getSourceRange().getBegin(),
+           diag::err_not_reference_to_const_init,
+           T1.getAsString(), 
+           InitLvalue != Expr::LV_Valid? "temporary" : "value",
+           T2.getAsString(), Init->getSourceRange());
+    return true;
+  }
+
+  //       -- If the initializer expression is an rvalue, with T2 a
+  //          class type, and “cv1 T1” is reference-compatible with
+  //          “cv2 T2,” the reference is bound in one of the
+  //          following ways (the choice is implementation-defined):
+  //
+  //          -- The reference is bound to the object represented by
+  //             the rvalue (see 3.10) or to a sub-object within that
+  //             object.
+  //
+  //          -- A temporary of type “cv1 T2” [sic] is created, and
+  //             a constructor is called to copy the entire rvalue
+  //             object into the temporary. The reference is bound to
+  //             the temporary or to a sub-object within the
+  //             temporary.
+  //
+  //
+  //          The constructor that would be used to make the copy
+  //          shall be callable whether or not the copy is actually
+  //          done.
+  //
+  // Note that C++0x [dcl.ref.init]p5 takes away this implementation
+  // freedom, so we will always take the first option and never build
+  // a temporary in this case. FIXME: We will, however, have to check
+  // for the presence of a copy constructor in C++98/03 mode.
+  if (InitLvalue != Expr::LV_Valid && T2->isRecordType() &&
+      RefRelationship >= Ref_Compatible) {
+    if (!Complain) {
+      // FIXME: Binding to a subobject of the rvalue is going to require
+      // more AST annotation than this.
+      ImpCastExprToType(Init, T1);
+    }
+    return false;
+  }
+
+  //       -- Otherwise, a temporary of type “cv1 T1” is created and
+  //          initialized from the initializer expression using the
+  //          rules for a non-reference copy initialization (8.5). The
+  //          reference is then bound to the temporary. If T1 is
+  //          reference-related to T2, cv1 must be the same
+  //          cv-qualification as, or greater cv-qualification than,
+  //          cv2; otherwise, the program is ill-formed.
+  if (RefRelationship == Ref_Related) {
+    // If cv1 == cv2 or cv1 is a greater cv-qualified than cv2, then
+    // we would be reference-compatible or reference-compatible with
+    // added qualification. But that wasn't the case, so the reference
+    // initialization fails.
+    if (Complain)
+      Diag(Init->getSourceRange().getBegin(),
+           diag::err_reference_init_drops_quals,
+           T1.getAsString(), 
+           InitLvalue != Expr::LV_Valid? "temporary" : "value",
+           T2.getAsString(), Init->getSourceRange());
+    return true;
+  }
+
+  // Actually try to convert the initializer to T1.
+  if (Complain)
+    return PerformImplicitConversion(Init, T1);
+  else
+    return (TryImplicitConversion(Init, T1).ConversionKind
+              == ImplicitConversionSequence::BadConversion);
+}
index a4fae9117edec10c975b03d648fe7d1487150f6d..831765376b6a0f91a7f91adf30b3d54cafecc940 100644 (file)
@@ -1174,14 +1174,11 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc,
         Arg = new CXXDefaultArgExpr(FDecl->getParamDecl(i));
       QualType ArgType = Arg->getType();
 
-      // Compute implicit casts from the operand to the formal argument type.
-      AssignConvertType ConvTy =
-        CheckSingleAssignmentConstraints(ProtoArgType, Arg);
-      TheCall->setArg(i, Arg);
-
-      if (DiagnoseAssignmentResult(ConvTy, Arg->getLocStart(), ProtoArgType,
-                                   ArgType, Arg, "passing"))
+      // Pass the argument.
+      if (PerformCopyInitialization(Arg, ProtoArgType, "passing"))
         return true;
+
+      TheCall->setArg(i, Arg);
     }
     
     // If this is a variadic call, handle args passed through "...".
index 7a5e2dd4e91ff38697f70b34dccf59aa3fa65438..209914b45c3b292e483dfd9610a8cb942b998187 100644 (file)
@@ -567,7 +567,7 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) {
 bool 
 Sema::PerformImplicitConversion(Expr *&From, QualType ToType)
 {
-  ImplicitConversionSequence ICS = TryCopyInitialization(From, ToType);
+  ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType);
   switch (ICS.ConversionKind) {
   case ImplicitConversionSequence::StandardConversion:
     if (PerformImplicitConversion(From, ToType, ICS.Standard))
index a676b64ff8685ced6083256764452e55208af53b..ce48a43111cf30cbd2f2c35663920f2fe29ed896 100644 (file)
@@ -44,33 +44,32 @@ void BasePaths::clear() {
   ScratchPath.clear();
 }
 
-/// IsDerivedFrom - Determine whether the class type Derived is
-/// derived from the class type Base, ignoring qualifiers on Base and
-/// Derived. This routine does not assess whether an actual conversion
-/// from a Derived* to a Base* is legal, because it does not account
-/// for ambiguous conversions or conversions to private/protected bases.
+/// IsDerivedFrom - Determine whether the type Derived is derived from
+/// the type Base, ignoring qualifiers on Base and Derived. This
+/// routine does not assess whether an actual conversion from a
+/// Derived* to a Base* is legal, because it does not account for
+/// ambiguous conversions or conversions to private/protected bases.
 bool Sema::IsDerivedFrom(QualType Derived, QualType Base) {
   BasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false);
   return IsDerivedFrom(Derived, Base, Paths);
 }
 
-/// IsDerivedFrom - Determine whether the class type Derived is
-/// derived from the class type Base, ignoring qualifiers on Base and
-/// Derived. This routine does not assess whether an actual conversion
-/// from a Derived* to a Base* is legal, because it does not account
-/// for ambiguous conversions or conversions to private/protected
+/// IsDerivedFrom - Determine whether the type Derived is derived from
+/// the type Base, ignoring qualifiers on Base and Derived. This
+/// routine does not assess whether an actual conversion from a
+/// Derived* to a Base* is legal, because it does not account for
+/// ambiguous conversions or conversions to private/protected
 /// bases. This routine will use Paths to determine if there are
 /// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record
-/// information about all of the paths (if
-/// @c Paths.isRecordingPaths()).
+/// information about all of the paths (if @c Paths.isRecordingPaths()).
 bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) {
   bool FoundPath = false;
   
   Derived = Context.getCanonicalType(Derived).getUnqualifiedType();
   Base = Context.getCanonicalType(Base).getUnqualifiedType();
   
-  assert(Derived->isRecordType() && "IsDerivedFrom requires a class type");
-  assert(Base->isRecordType() && "IsDerivedFrom requires a class type");
+  if (!Derived->isRecordType() || !Base->isRecordType())
+    return false;
 
   if (Derived == Base)
     return false;
index 7c7836568633739b217f00753402aaa0dd9c3d69..8e765485b11e8b0c12f3adc54e37fdcb5365e309 100644 (file)
@@ -312,11 +312,10 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD,
   }
 }
 
-/// TryCopyInitialization - Attempt to copy-initialize a value of the
-/// given type (ToType) from the given expression (Expr), as one would
-/// do when copy-initializing a function parameter. This function
-/// returns an implicit conversion sequence that can be used to
-/// perform the initialization. Given
+/// TryImplicitConversion - Attempt to perform an implicit conversion
+/// from the given expression (Expr) to the given type (ToType). This
+/// function returns an implicit conversion sequence that can be used
+/// to perform the initialization. Given
 ///
 ///   void f(float f);
 ///   void g(int i) { f(i); }
@@ -332,7 +331,7 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD,
 /// but will instead return an implicit conversion sequence of kind
 /// "BadConversion".
 ImplicitConversionSequence
-Sema::TryCopyInitialization(Expr* From, QualType ToType)
+Sema::TryImplicitConversion(Expr* From, QualType ToType)
 {
   ImplicitConversionSequence ICS;
 
@@ -343,32 +342,6 @@ Sema::TryCopyInitialization(Expr* From, QualType ToType)
   ICS.Standard.Deprecated = false;
   ICS.Standard.FromTypePtr = FromType.getAsOpaquePtr();
 
-  if (const ReferenceType *ToTypeRef = ToType->getAsReferenceType()) {
-    // FIXME: This is a hack to deal with the initialization of
-    // references the way that the C-centric code elsewhere deals with
-    // references, by only allowing them if the referred-to type is
-    // exactly the same. This means that we're only handling the
-    // direct-binding case. The code will be replaced by an
-    // implementation of C++ 13.3.3.1.4 once we have the
-    // initialization of references implemented.
-    QualType ToPointee = Context.getCanonicalType(ToTypeRef->getPointeeType());
-
-    // Get down to the canonical type that we're converting from.
-    if (const ReferenceType *FromTypeRef = FromType->getAsReferenceType())
-      FromType = FromTypeRef->getPointeeType();
-    FromType = Context.getCanonicalType(FromType);
-
-    ICS.Standard.First = ICK_Identity;
-    ICS.Standard.Second = ICK_Identity;
-    ICS.Standard.Third = ICK_Identity;
-    ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr();
-
-    if (FromType != ToPointee)
-      ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
-
-    return ICS;
-  }
-
   // The first conversion can be an lvalue-to-rvalue conversion,
   // array-to-pointer conversion, or function-to-pointer conversion
   // (C++ 4p1).
@@ -482,18 +455,37 @@ Sema::TryCopyInitialization(Expr* From, QualType ToType)
     ICS.Standard.Second = ICK_Identity;
   }
 
+  QualType CanonFrom;
+  QualType CanonTo;
   // The third conversion can be a qualification conversion (C++ 4p1).
   if (IsQualificationConversion(FromType, ToType)) {
     ICS.Standard.Third = ICK_Qualification;
     FromType = ToType;
+    CanonFrom = Context.getCanonicalType(FromType);
+    CanonTo = Context.getCanonicalType(ToType);
   } else {
     // No conversion required
     ICS.Standard.Third = ICK_Identity;
+
+    // C++ [dcl.init]p14 last bullet:
+    //   Note: an expression of type "cv1 T" can initialize an object
+    //   of type “cv2 T” independently of the cv-qualifiers cv1 and
+    //   cv2. -- end note]
+    //
+    // FIXME: Where is the normative text?
+    CanonFrom = Context.getCanonicalType(FromType);
+    CanonTo = Context.getCanonicalType(ToType);    
+    if (!FromType->isRecordType() &&
+        CanonFrom.getUnqualifiedType() == CanonTo.getUnqualifiedType() &&
+        CanonFrom.getCVRQualifiers() != CanonTo.getCVRQualifiers()) {
+      FromType = ToType;
+      CanonFrom = CanonTo;
+    }
   }
 
   // If we have not converted the argument type to the parameter type,
   // this is a bad conversion sequence.
-  if (Context.getCanonicalType(FromType) != Context.getCanonicalType(ToType))
+  if (CanonFrom != CanonTo)
     ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
 
   ICS.Standard.ToTypePtr = FromType.getAsOpaquePtr();
@@ -1054,6 +1046,63 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
   return ImplicitConversionSequence::Indistinguishable;
 }
 
+/// TryCopyInitialization - Try to copy-initialize a value of type
+/// ToType from the expression From. Return the implicit conversion
+/// sequence required to pass this argument, which may be a bad
+/// conversion sequence (meaning that the argument cannot be passed to
+/// a parameter of this type). This is user for argument passing, 
+ImplicitConversionSequence 
+Sema::TryCopyInitialization(Expr *From, QualType ToType) {
+  if (!getLangOptions().CPlusPlus) {
+    // In C, argument passing is the same as performing an assignment.
+    AssignConvertType ConvTy =
+      CheckSingleAssignmentConstraints(ToType, From);
+    ImplicitConversionSequence ICS;
+    if (getLangOptions().NoExtensions? ConvTy != Compatible
+                                     : ConvTy == Incompatible)
+      ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
+    else
+      ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
+    return ICS;
+  } else if (ToType->isReferenceType()) {
+    ImplicitConversionSequence ICS;
+    if (CheckReferenceInit(From, ToType, /*Complain=*/false))
+      ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
+    else
+      ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
+    return ICS;
+  } else {
+    return TryImplicitConversion(From, ToType);
+  }
+}
+
+/// PerformArgumentPassing - Pass the argument Arg into a parameter of
+/// type ToType. Returns true (and emits a diagnostic) if there was
+/// an error, returns false if the initialization succeeded.
+bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, 
+                                     const char* Flavor) {
+  if (!getLangOptions().CPlusPlus) {
+    // In C, argument passing is the same as performing an assignment.
+    QualType FromType = From->getType();
+    AssignConvertType ConvTy =
+      CheckSingleAssignmentConstraints(ToType, From);
+
+    return DiagnoseAssignmentResult(ConvTy, From->getLocStart(), ToType,
+                                    FromType, From, Flavor);
+  } else if (ToType->isReferenceType()) {
+    return CheckReferenceInit(From, ToType);
+  } else {
+    if (PerformImplicitConversion(From, ToType))
+      return Diag(From->getSourceRange().getBegin(),
+                  diag::err_typecheck_convert_incompatible,
+                  ToType.getAsString(), From->getType().getAsString(),
+                  Flavor,
+                  From->getSourceRange());
+    else
+      return false;
+  }
+}
+
 /// AddOverloadCandidate - Adds the given function to the set of
 /// candidate functions, using the given function call arguments.
 void 
index f75758ebb0a78c87982616507f279ea1e7d07dfa..e7ccdda27298ec9578c6986700899a3657cc9e76 100644 (file)
@@ -693,7 +693,7 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
   return new BreakStmt(BreakLoc);
 }
 
-/// ActOnBlockReturnStmt - Utilty routine to figure out block's return type.
+/// ActOnBlockReturnStmt - Utility routine to figure out block's return type.
 ///
 Action::StmtResult
 Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
@@ -782,10 +782,11 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprTy *rex) {
   // C99 6.8.6.4p3(136): The return statement is not an assignment. The 
   // overlap restriction of subclause 6.5.16.1 does not apply to the case of 
   // function return.  
-  AssignConvertType ConvTy = CheckSingleAssignmentConstraints(FnRetType, 
-                                                              RetValExp);
-  if (DiagnoseAssignmentResult(ConvTy, ReturnLoc, FnRetType,
-                               RetValType, RetValExp, "returning"))
+
+  // In C++ the return statement is handled via a copy initialization.
+  // the C version of which boils down to
+  // CheckSingleAssignmentConstraints.
+  if (PerformCopyInitialization(RetValExp, FnRetType, "returning"))
     return true;
   
   if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
index 4f3b58c5d53ecfaff6309faf271dd542950a60b2..8d65defe7df69751575cdc7989b858c0d2760ca3 100644 (file)
@@ -3,6 +3,8 @@
 extern char *bork;
 char *& bar = bork;
 
+int val;
+
 void foo(int &a) {
 }
 
@@ -11,7 +13,7 @@ typedef int & A;
 void g(const A aref) {
 }
 
-int & const X; // expected-error {{'const' qualifier may not be applied to a reference}}
-int & volatile Y; // expected-error {{'volatile' qualifier may not be applied to a reference}}
-int & const volatile Z; /* expected-error {{'const' qualifier may not be applied}} \
+int & const X = val; // expected-error {{'const' qualifier may not be applied to a reference}}
+int & volatile Y = val; // expected-error {{'volatile' qualifier may not be applied to a reference}}
+int & const volatile Z = val; /* expected-error {{'const' qualifier may not be applied}} \
                            expected-error {{'volatile' qualifier may not be applied}} */
index 1bd64966128b50b617a769ad54e2fe29dbbebe52..663474d32829a9fc4f8676f9eb2df5fbecff6391 100644 (file)
@@ -214,3 +214,14 @@ void test_derived(B* b, B const* bc, C* c, const C* cc, void* v, D* d) {
 
   char* d8 = derived3(d);
 }
+
+// Test overloading of references. 
+// (FIXME: tests binding to determine candidate sets, not overload 
+//  resolution per se).
+int* intref(int&);
+float* intref(const int&);
+
+void intref_test() {
+  float* ir1 = intref(5);
+  float* ir2 = intref(5.5);
+}
index 3637573b445e171c83a06f7cd89cdbc32bd8b568..eebc3e8f7b3f45b2cc6327f88bd05749ab4fba4e 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: clang -fsyntax-only %s
+// RUN: clang -fsyntax-only -verify %s
 int g(int);
 
 void f() {
@@ -7,10 +7,10 @@ void f() {
   r = 1;
   int *p = &r;
   int &rr = r;
-  int (&rg)(int) = g;
+  int (&rg)(int) = g; // expected-warning{{statement was disambiguated as declaration}}
   rg(i);
   int a[3];
-  int (&ra)[3] = a;
+  int (&ra)[3] = a;  // expected-warning{{statement was disambiguated as declaration}}
   ra[1] = i;
   int *Q;
   int *& P = Q;
@@ -24,5 +24,57 @@ void test2() {
 
 
     int c[3];
-    int (&rc)[3] = c;
+    int (&rc)[3] = c; // expected-warning{{statement was disambiguated as declaration}}
+}
+
+// C++ [dcl.init.ref]p5b1
+struct A { };
+struct B : A { } b;
+
+void test3() {
+  double d = 2.0;
+  double& rd = d; // rd refers to d
+  const double& rcd = d; // rcd refers to d
+
+  A& ra = b; // ra refers to A subobject in b
+  const A& rca = b; // rca refers to A subobject in b
+}
+
+B fB();
+
+// C++ [dcl.init.ref]p5b2
+void test4() {
+  double& rd2 = 2.0; // expected-error{{non-const reference to type 'double' cannot be initialized with a temporary of type 'double'}}
+  int i = 2;
+  double& rd3 = i; // expected-error{{non-const reference to type 'double' cannot be initialized with a value of type 'int'}}
+
+  const A& rca = fB();
+}
+
+void test5() {
+  const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0
+  const volatile int cvi = 1;
+  const int& r = cvi; // expected-error{{initialization of reference to type 'int const' with a value of type 'int const volatile' drops qualifiers}}
+}
+
+// C++ [dcl.init.ref]p3
+int& test6(int& x) {
+  int& yo; // expected-error{{declaration of reference variable 'yo' requires an initializer}}
+
+
+  const int val; // expected-error{{declaration of const variable 'val' requires an initializer}}
+
+  return x;
+}
+int& not_initialized_error; // expected-error{{declaration of reference variable 'not_initialized_error' requires an initializer}}
+extern int& not_initialized_okay;
+
+class Test6 {
+  int& okay;
+};
+
+struct C : B, A { };
+
+void test7(C& c) {
+  A& a1 = c; // expected-error {{ambiguous conversion from derived class 'struct C' to base class 'struct A':}}
 }