]> granicus.if.org Git - clang/commitdiff
Initial step toward supporting qualification conversions (C++ 4.4).
authorDouglas Gregor <dgregor@apple.com>
Tue, 21 Oct 2008 23:43:52 +0000 (23:43 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 21 Oct 2008 23:43:52 +0000 (23:43 +0000)
Changes:
  - Sema::IsQualificationConversion determines whether we have a qualification
    conversion.
  - Sema::CheckSingleAssignment constraints now follows the C++ rules in C++,
    performing an implicit conversion from the right-hand side to the type of
    the left-hand side rather than checking based on the C notion of
    "compatibility". We now rely on the implicit-conversion code to
    determine whether the conversion can happen or
    not. Sema::TryCopyInitialization has an ugly reference-related
    hack to cope with the initialization of references, for now.
  - When building DeclRefExprs, strip away the reference type, since
    there are no expressions whose type is a reference. We'll need to
    do this throughout Sema.
  - Expr::isLvalue now permits functions to be lvalues in C++ (but not
  in C).

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

include/clang/AST/Type.h
lib/AST/Expr.cpp
lib/Sema/Sema.h
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaType.cpp
test/SemaCXX/decl-expr-ambiguity.cpp
test/SemaCXX/qualification-conversion.cpp [new file with mode: 0644]

index e2ba317d05da10b479bc541bf25f791c8a64cb9c..b11f7112558a1fbe6d306196586d82fb6b1fdc59 100644 (file)
@@ -160,6 +160,27 @@ public:
   
   inline QualType getUnqualifiedType() const;
   
+  /// isMoreQualifiedThan - Determine whether this type is more
+  /// qualified than the Other type. For example, "const volatile int"
+  /// is more qualified than "const int", "volatile int", and
+  /// "int". However, it is not more qualified than "const volatile
+  /// int".
+  bool isMoreQualifiedThan(QualType Other) const {
+    unsigned MyQuals = this->getCVRQualifiers();
+    unsigned OtherQuals = Other.getCVRQualifiers();
+    return MyQuals != OtherQuals && (MyQuals | OtherQuals) == MyQuals;
+  }
+
+  /// isAtLeastAsQualifiedAs - Determine whether this type is at last
+  /// as qualified as the Other type. For example, "const volatile
+  /// int" is at least as qualified as "const int", "volatile int",
+  /// "int", and "const volatile int".
+  bool isAtLeastAsQualifiedAs(QualType Other) const {
+    unsigned MyQuals = this->getCVRQualifiers();
+    unsigned OtherQuals = Other.getCVRQualifiers();
+    return MyQuals | OtherQuals == MyQuals;
+  }
+
   /// operator==/!= - Indicate whether the specified types and qualifiers are
   /// identical.
   bool operator==(const QualType &RHS) const {
index 943cd9a05775dcead92e5536279c6f32be474e29..d957e33aa126b8daf30b8b4017d2e5cbef7b35f1 100644 (file)
@@ -15,6 +15,7 @@
 #include "clang/AST/APValue.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/TargetInfo.h"
@@ -334,14 +335,17 @@ bool Expr::hasLocalSideEffect() const {
 ///  - reference type [C++ [expr]]
 ///
 Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
-  // first, check the type (C99 6.3.2.1)
-  if (TR->isFunctionType()) // from isObjectType()
+  // first, check the type (C99 6.3.2.1). Expressions with function
+  // type in C are not lvalues, but they can be lvalues in C++.
+  if (!Ctx.getLangOptions().CPlusPlus && TR->isFunctionType())
     return LV_NotObjectType;
 
   // Allow qualified void which is an incomplete type other than void (yuck).
   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;
 
@@ -356,7 +360,11 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
     return LV_Valid;
   case DeclRefExprClass: { // C99 6.5.1p2
     const Decl *RefdDecl = cast<DeclRefExpr>(this)->getDecl();
-    if (isa<VarDecl>(RefdDecl) || isa<ImplicitParamDecl>(RefdDecl))
+    if (isa<VarDecl>(RefdDecl) || 
+        isa<ImplicitParamDecl>(RefdDecl) ||
+        // C++ 3.10p2: An lvalue refers to an object or function.
+        isa<FunctionDecl>(RefdDecl) || 
+        isa<OverloadedFunctionDecl>(RefdDecl))
       return LV_Valid;
     break;
   }
index 039571f9fe234629cbdc0c247f692dd44dc09fa6..d53e200ba9f4a1dc08dfb4bea28287023981c70a 100644 (file)
@@ -259,7 +259,7 @@ public:
   QualType ConvertDeclSpecToType(const DeclSpec &DS);
   void ProcessTypeAttributeList(QualType &Result, const AttributeList *AL);
   QualType GetTypeForDeclarator(Declarator &D, Scope *S);
-
+  QualType GetNonReferenceType(QualType Type);
   
   QualType ObjCGetTypeForMethodDefinition(DeclTy *D);
 
@@ -371,6 +371,7 @@ private:
   bool IsFloatingPointPromotion(QualType FromType, QualType ToType);
   bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
                            QualType& ConvertedType);
+  bool IsQualificationConversion(QualType FromType, QualType ToType);
 
   ImplicitConversionSequence::CompareKind 
   CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1,
index 7a3c6bdeb15763094bf039da6f19eff0f5a10951..3ead4308d2790bf42b2dc25ef6f35074ee6993b3 100644 (file)
@@ -450,7 +450,7 @@ Sema::ExprResult Sema::ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
   }
   // If this reference is not in a block or if the referenced variable is
   // within the block, create a normal DeclRefExpr.
-  return new DeclRefExpr(VD, VD->getType(), Loc);
+  return new DeclRefExpr(VD, GetNonReferenceType(VD->getType()), Loc);
 }
 
 Sema::ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc,
@@ -1565,8 +1565,7 @@ Sema::CheckPointerTypesForAssignment(QualType lhsType, QualType rhsType) {
   // 3 & 4 (below). ...and the type *pointed to* by the left has all the 
   // qualifiers of the type *pointed to* by the right; 
   // FIXME: Handle ASQualType
-  if ((lhptee.getCVRQualifiers() & rhptee.getCVRQualifiers()) != 
-       rhptee.getCVRQualifiers())
+  if (!lhptee.isAtLeastAsQualifiedAs(rhptee))
     ConvTy = CompatiblePointerDiscardsQualifiers;
 
   // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or 
@@ -1766,6 +1765,28 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
 
 Sema::AssignConvertType
 Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
+  if (getLangOptions().CPlusPlus) {
+    if (!lhsType->isRecordType()) {
+      // C++ 5.17p3: If the left operand is not of class type, the
+      // expression is implicitly converted (C++ 4) to the
+      // cv-unqualified type of the left operand.
+      ImplicitConversionSequence ICS 
+        = TryCopyInitialization(rExpr, lhsType.getUnqualifiedType());
+      if (ICS.ConversionKind == ImplicitConversionSequence::BadConversion) {
+        // No implicit conversion available; we cannot perform this
+        // assignment.
+        return Incompatible;
+      } else {
+        // Perform the appropriate cast to the right-handle side.
+        ImpCastExprToType(rExpr, lhsType.getUnqualifiedType());
+        return Compatible;
+      }
+    }
+
+    // FIXME: Currently, we fall through and treat C++ classes like C
+    // structures.
+  }
+
   // C99 6.5.16.1p1: the left operand is a pointer and the right is
   // a null pointer constant.
   if ((lhsType->isPointerType() || lhsType->isObjCQualifiedIdType() ||
index 88c209b6e00c99b2c62e6a9d86e9aabfc796c0bd..2696c985231fdc103d6c81c635374c9121151c4b 100644 (file)
@@ -318,6 +318,32 @@ 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).
@@ -432,10 +458,7 @@ Sema::TryCopyInitialization(Expr* From, QualType ToType)
   }
 
   // The third conversion can be a qualification conversion (C++ 4p1).
-  // FIXME: CheckPointerTypesForAssignment isn't the right way to
-  // determine whether we have a qualification conversion.
-  if (Context.getCanonicalType(FromType) != Context.getCanonicalType(ToType)
-      && CheckPointerTypesForAssignment(ToType, FromType) == Compatible) {
+  if (IsQualificationConversion(FromType, ToType)) {
     ICS.Standard.Third = ICK_Qualification;
     FromType = ToType;
   } else {
@@ -618,6 +641,74 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
   return false;
 }
 
+/// IsQualificationConversion - Determines whether the conversion from
+/// an rvalue of type FromType to ToType is a qualification conversion
+/// (C++ 4.4).
+bool 
+Sema::IsQualificationConversion(QualType FromType, QualType ToType)
+{
+  FromType = Context.getCanonicalType(FromType);
+  ToType = Context.getCanonicalType(ToType);
+
+  // If FromType and ToType are the same type, this is not a
+  // qualification conversion.
+  if (FromType == ToType)
+    return false;
+    
+  // (C++ 4.4p4):
+  //   A conversion can add cv-qualifiers at levels other than the first
+  //   in multi-level pointers, subject to the following rules: [...]
+  bool PreviousToQualsIncludeConst = true;
+  bool UnwrappedPointer;
+  bool UnwrappedAnyPointer = false;
+  do {
+    // Within each iteration of the loop, we check the qualifiers to
+    // determine if this still looks like a qualification
+    // conversion. Then, if all is well, we unwrap one more level of
+    // pointers (FIXME: or pointers-to-members) and do it all again
+    // until there are no more pointers or pointers-to-members left to
+    // unwrap.
+    UnwrappedPointer = false;
+
+    //   -- the pointer types are similar.
+    const PointerType *FromPtrType = FromType->getAsPointerType(),
+                      *ToPtrType   = ToType->getAsPointerType();
+    if (FromPtrType && ToPtrType) {
+      // The pointer types appear similar. Look at their pointee types.
+      FromType = FromPtrType->getPointeeType();
+      ToType = ToPtrType->getPointeeType();
+      UnwrappedPointer = true;
+      UnwrappedAnyPointer = true;
+    } 
+
+    // FIXME: Cope with pointer-to-member types.
+
+    //   -- for every j > 0, if const is in cv 1,j then const is in cv
+    //      2,j, and similarly for volatile.
+    if (FromType.isMoreQualifiedThan(ToType))
+      return false;
+
+    //   -- if the cv 1,j and cv 2,j are different, then const is in
+    //      every cv for 0 < k < j.
+    if (FromType.getCVRQualifiers() != ToType.getCVRQualifiers()
+       && !PreviousToQualsIncludeConst)
+      return false;
+
+    // Keep track of whether all prior cv-qualifiers in the "to" type
+    // include const.
+    PreviousToQualsIncludeConst 
+      = PreviousToQualsIncludeConst && ToType.isConstQualified();
+  } while (UnwrappedPointer);
+
+  // We are left with FromType and ToType being the pointee types
+  // after unwrapping the original FromType and ToType the same number
+  // of types. If we unwrapped any pointers, and if FromType and
+  // ToType have the same unqualified type (since we checked
+  // qualifiers above), then this is a qualification conversion.
+  return UnwrappedAnyPointer &&
+    FromType.getUnqualifiedType() == ToType.getUnqualifiedType();
+}
+
 /// CompareImplicitConversionSequences - Compare two implicit
 /// conversion sequences to determine whether one is better than the
 /// other or if they are indistinguishable (C++ 13.3.3.2).
index f1edabfa7e9c83a590e1a7aa3f3e169734dc67ac..942d7063a1d8ac6044ff300d57b5a431579ba0f4 100644 (file)
@@ -500,6 +500,23 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) {
   return T;
 }
 
+/// GetNonReferenceType - If Type is a reference type (e.g., const
+/// int&), returns the type that the reference refers to ("const
+/// int"). Otherwise, returns the type itself. This routine is used
+/// throughout to implement C++ 5p6:
+///
+///   If an expression initially has the type "reference to T" (8.3.2,
+///   8.5.3), the type is adjusted to "T" prior to any further
+///   analysis, the expression designates the object or function
+///   denoted by the reference, and the expression is an lvalue.
+QualType Sema::GetNonReferenceType(QualType Type)
+{
+  if (const ReferenceType *RefType = Type->getAsReferenceType())
+    return RefType->getPointeeType();
+  else
+    return Type;
+}
+
 /// ObjCGetTypeForMethodDefinition - Builds the type for a method definition
 /// declarator
 QualType Sema::ObjCGetTypeForMethodDefinition(DeclTy *D) {
index b2c3ea7b5ac799964bee204008de58e153fbaf79..5a76b933425e914f74122d87cf1fb9b744327325 100644 (file)
@@ -38,6 +38,6 @@ void fn(int(C)) { } // void fn(int(*fp)(C c)) { }
 int g(C);
 
 void foo() {
-  fn(1); // expected-error {{incompatible integer to pointer conversion passing 'int', expected 'int (*)(class C)'}}
+  fn(1); // expected-error {{incompatible type passing 'int', expected 'int (*)(class C)'}}
   fn(g); // OK
 }
diff --git a/test/SemaCXX/qualification-conversion.cpp b/test/SemaCXX/qualification-conversion.cpp
new file mode 100644 (file)
index 0000000..d467d31
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: clang -fsyntax-only -pedantic -verify %s 
+int* quals1(int const * p);
+int* quals2(int const * const * pp);
+int* quals3(int const * * const * ppp);
+
+void test_quals(int * p, int * * pp, int * * * ppp) {
+  int const * const * pp2 = pp; 
+  quals1(p);
+  quals2(pp);
+  quals3(ppp); // expected-error {{ incompatible type passing 'int ***', expected 'int const **const *' }}
+}