[cfe-commits] r39369 - in /cfe/cfe/trunk: AST/Sema.cpp AST/Sema.h AST/SemaExpr.cpp AST/Type.cpp Sema/Sema.cpp Sema/Sema.h Sema/SemaExpr.cpp include/clang/AST/Expr.h include/clang/AST/Type.h include/clang/Basic/DiagnosticKinds.def

Steve Naroff snaroff at apple.com
Wed Jul 11 09:43:41 PDT 2007


Author: snaroff
Date: Wed Jul 11 11:43:41 2007
New Revision: 39369

URL: http://llvm.org/viewvc/llvm-project?rev=39369&view=rev
Log:
Bug #:
Submitted by:
Reviewed by:
Implement ++/-- typechecking for pre/post unary expressions. This includes:

- added isLvalue, isModifiableLvalue (on TypeRef, Type, and RecordType).
- added isRealType, isRealFloatingType, isComplexType.
- hacked Diag to take a TypeRef (I was sick of writing the 2 line "setup":-)
In addition, this will likely lead to less bugs...I already had written code
that was doing a getAsString on "Type" (which is wrong...since it doesn't include
any qualifiers).
- Changed UnaryOperator to take a TypeRef...pass it the right stuff.
- Removed redundant ternary expressions in several predicates.
- A couple diagnostics.

Modified:
    cfe/cfe/trunk/AST/Sema.cpp
    cfe/cfe/trunk/AST/Sema.h
    cfe/cfe/trunk/AST/SemaExpr.cpp
    cfe/cfe/trunk/AST/Type.cpp
    cfe/cfe/trunk/Sema/Sema.cpp
    cfe/cfe/trunk/Sema/Sema.h
    cfe/cfe/trunk/Sema/SemaExpr.cpp
    cfe/cfe/trunk/include/clang/AST/Expr.h
    cfe/cfe/trunk/include/clang/AST/Type.h
    cfe/cfe/trunk/include/clang/Basic/DiagnosticKinds.def

Modified: cfe/cfe/trunk/AST/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/AST/Sema.cpp?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/AST/Sema.cpp (original)
+++ cfe/cfe/trunk/AST/Sema.cpp Wed Jul 11 11:43:41 2007
@@ -31,6 +31,13 @@
   return true;
 }
 
+bool Sema::Diag(SourceLocation Loc, unsigned DiagID, TypeRef t) {
+  std::string Name;
+  t.getAsString(Name);
+  PP.Diag(Loc, DiagID, Name);
+  return true;
+}
+
 bool Sema::Diag(const LexerToken &Tok, unsigned DiagID, const std::string &M) {
   Diag(Tok.getLocation(), DiagID, M);
   return true;

Modified: cfe/cfe/trunk/AST/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/AST/Sema.h?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/AST/Sema.h (original)
+++ cfe/cfe/trunk/AST/Sema.h Wed Jul 11 11:43:41 2007
@@ -58,6 +58,7 @@
             const std::string &Msg = std::string());
   bool Diag(const LexerToken &Tok, unsigned DiagID,
             const std::string &M = std::string());
+  bool Diag(SourceLocation Loc, unsigned DiagID, TypeRef t);
   
   //===--------------------------------------------------------------------===//
   // Type Analysis / Processing: SemaType.cpp.

Modified: cfe/cfe/trunk/AST/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/AST/SemaExpr.cpp?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/AST/SemaExpr.cpp (original)
+++ cfe/cfe/trunk/AST/SemaExpr.cpp Wed Jul 11 11:43:41 2007
@@ -173,8 +173,22 @@
     //Opc = UnaryOperator::Extension;
     //break;
   }
+  TypeRef type = ((Expr *)Input)->getTypeRef();
 
-  return new UnaryOperator((Expr*)Input, Opc);
+  assert(!type.isNull() && "no type for prefix unary expression");
+
+  if (Opc == UnaryOperator::PreInc || Opc == UnaryOperator::PreDec) {
+    // C99 6.5.3.1: isRealType excludes complex (GCC allows complex).
+    if (!type->isRealType() && !type->isPointerType())
+      return Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement, type);    
+
+    // At this point, we know we have a real or pointer type. As a result, the
+    // following predicate is overkill (i.e. it will check for types we know we
+    // don't have in this context). Nevertheless, we model the C99 spec closely.
+    if (!type.isModifiableLvalue())
+      return Diag(OpLoc, diag::err_typecheck_not_modifiable, type);
+  }
+  return new UnaryOperator((Expr*)Input, Opc, type);
 }
 
 Action::ExprResult Sema::
@@ -214,8 +228,21 @@
   case tok::plusplus:   Opc = UnaryOperator::PostInc; break;
   case tok::minusminus: Opc = UnaryOperator::PostDec; break;
   }
+  TypeRef type = ((Expr *)Input)->getTypeRef();
+
+  assert(!type.isNull() && "no type for postfix unary expression");
   
-  return new UnaryOperator((Expr*)Input, Opc);
+  // C99 6.5.2.4: isRealType excludes complex (GCC allows complex).
+  if (!type->isRealType() && !type->isPointerType())
+    return Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement, type);    
+
+  // At this point, we know we have a real or pointer type. As a result, the
+  // following predicate is overkill (i.e. it will check for types we know we
+  // don't have in this context). Nevertheless, we model the C99 spec closely.
+  if (!type.isModifiableLvalue())
+    return Diag(OpLoc, diag::err_typecheck_not_modifiable, type);
+    
+  return new UnaryOperator((Expr*)Input, Opc, type);
 }
 
 Action::ExprResult Sema::
@@ -253,11 +280,8 @@
     resultType = ary->getPointeeType();
     // in practice, the following check catches trying to index a pointer
     // to a function (e.g. void (*)(int)). Functions are not objects in c99.
-    if (!resultType->isObjectType()) {
-      std::string Name;
-      baseType->getAsString(Name);
-      Diag(LLoc, diag::err_typecheck_subscript_not_object, Name);    
-    }
+    if (!resultType->isObjectType())
+      return Diag(LLoc, diag::err_typecheck_subscript_not_object, baseType);    
   } 
   return new ArraySubscriptExpr((Expr*)Base, (Expr*)Idx, resultType);
 }

Modified: cfe/cfe/trunk/AST/Type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/AST/Type.cpp?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/AST/Type.cpp (original)
+++ cfe/cfe/trunk/AST/Type.cpp Wed Jul 11 11:43:41 2007
@@ -44,15 +44,15 @@
 }
 
 bool Type::isFunctionType() const {
-  return isa<FunctionType>(CanonicalType) ? true : false;
+  return isa<FunctionType>(CanonicalType);
 }
 
 bool Type::isPointerType() const {
-  return isa<PointerType>(CanonicalType) ? true : false;
+  return isa<PointerType>(CanonicalType);
 }
 
 bool Type::isArrayType() const {
-  return isa<ArrayType>(CanonicalType) ? true : false;
+  return isa<ArrayType>(CanonicalType);
 }
 
 bool Type::isStructureType() const {
@@ -96,6 +96,42 @@
   }
 }
 
+bool Type::isRealFloatingType() const {
+  switch (CanonicalType->getTypeClass()) {
+  default: return false;
+  case Builtin:
+    const BuiltinType *BT = static_cast<BuiltinType*>(CanonicalType);
+    return BT->getKind() >= BuiltinType::Float &&
+           BT->getKind() <= BuiltinType::LongDouble;
+  }
+}
+
+bool Type::isRealType() const {
+  // this is equivalent to (isIntegralType() || isRealFloatingType()).
+  switch (CanonicalType->getTypeClass()) { // inlined for performance
+  default: return false;
+  case Builtin:
+    const BuiltinType *BT = static_cast<BuiltinType*>(CanonicalType);
+    return BT->getKind() >= BuiltinType::Bool &&
+           BT->getKind() <= BuiltinType::LongDouble;
+  case Tagged:
+    const TagType *TT = static_cast<TagType*>(CanonicalType);
+    if (TT->getDecl()->getKind() == Decl::Enum)
+      return true;
+    return false;
+  }
+}
+
+bool Type::isComplexType() const {
+  switch (CanonicalType->getTypeClass()) {
+  default: return false;
+  case Builtin:
+    const BuiltinType *BT = static_cast<BuiltinType*>(CanonicalType);
+    return BT->getKind() >= BuiltinType::FloatComplex &&
+           BT->getKind() <= BuiltinType::LongDoubleComplex;
+  }
+}
+
 bool Type::isArithmeticType() const {
   switch (CanonicalType->getTypeClass()) {
   default: return false;
@@ -152,6 +188,42 @@
   }
 }
 
+/// isLvalue - C99 6.3.2.1: an lvalue is an expression with an object type or
+/// an incomplete type other than void.
+bool Type::isLvalue() const {
+  if (isObjectType())
+    return true;
+  else if (isIncompleteType())
+    return isVoidType() ? false : true;
+  else 
+    return false;    
+}
+
+/// isModifiableLvalue - C99 6.3.2.1: an lvalue that does not have array type,
+/// does not have an incomplete type, does not have a const-qualified type, and
+/// if it is a structure or union, does not have any member (including, 
+/// recursively, any member or element of all contained aggregates or unions)
+/// with a const-qualified type.
+
+bool TypeRef::isModifiableLvalue() const {
+  if (isConstQualified())
+    return false;
+  else
+    return getTypePtr()->isModifiableLvalue();
+}
+
+bool Type::isModifiableLvalue() const {
+  if (!isLvalue())
+    return false;
+    
+  if (isArrayType())
+    return false;
+  if (isIncompleteType())
+    return false;
+  if (const RecordType *r = dyn_cast<RecordType>(this))
+    return r->isModifiableLvalue();
+  return true;    
+}
 
 const char *BuiltinType::getName() const {
   switch (getKind()) {

Modified: cfe/cfe/trunk/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/Sema/Sema.cpp?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/Sema/Sema.cpp (original)
+++ cfe/cfe/trunk/Sema/Sema.cpp Wed Jul 11 11:43:41 2007
@@ -31,6 +31,13 @@
   return true;
 }
 
+bool Sema::Diag(SourceLocation Loc, unsigned DiagID, TypeRef t) {
+  std::string Name;
+  t.getAsString(Name);
+  PP.Diag(Loc, DiagID, Name);
+  return true;
+}
+
 bool Sema::Diag(const LexerToken &Tok, unsigned DiagID, const std::string &M) {
   Diag(Tok.getLocation(), DiagID, M);
   return true;

Modified: cfe/cfe/trunk/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/Sema/Sema.h?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/Sema/Sema.h (original)
+++ cfe/cfe/trunk/Sema/Sema.h Wed Jul 11 11:43:41 2007
@@ -58,6 +58,7 @@
             const std::string &Msg = std::string());
   bool Diag(const LexerToken &Tok, unsigned DiagID,
             const std::string &M = std::string());
+  bool Diag(SourceLocation Loc, unsigned DiagID, TypeRef t);
   
   //===--------------------------------------------------------------------===//
   // Type Analysis / Processing: SemaType.cpp.

Modified: cfe/cfe/trunk/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/Sema/SemaExpr.cpp?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/Sema/SemaExpr.cpp (original)
+++ cfe/cfe/trunk/Sema/SemaExpr.cpp Wed Jul 11 11:43:41 2007
@@ -173,8 +173,22 @@
     //Opc = UnaryOperator::Extension;
     //break;
   }
+  TypeRef type = ((Expr *)Input)->getTypeRef();
 
-  return new UnaryOperator((Expr*)Input, Opc);
+  assert(!type.isNull() && "no type for prefix unary expression");
+
+  if (Opc == UnaryOperator::PreInc || Opc == UnaryOperator::PreDec) {
+    // C99 6.5.3.1: isRealType excludes complex (GCC allows complex).
+    if (!type->isRealType() && !type->isPointerType())
+      return Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement, type);    
+
+    // At this point, we know we have a real or pointer type. As a result, the
+    // following predicate is overkill (i.e. it will check for types we know we
+    // don't have in this context). Nevertheless, we model the C99 spec closely.
+    if (!type.isModifiableLvalue())
+      return Diag(OpLoc, diag::err_typecheck_not_modifiable, type);
+  }
+  return new UnaryOperator((Expr*)Input, Opc, type);
 }
 
 Action::ExprResult Sema::
@@ -214,8 +228,21 @@
   case tok::plusplus:   Opc = UnaryOperator::PostInc; break;
   case tok::minusminus: Opc = UnaryOperator::PostDec; break;
   }
+  TypeRef type = ((Expr *)Input)->getTypeRef();
+
+  assert(!type.isNull() && "no type for postfix unary expression");
   
-  return new UnaryOperator((Expr*)Input, Opc);
+  // C99 6.5.2.4: isRealType excludes complex (GCC allows complex).
+  if (!type->isRealType() && !type->isPointerType())
+    return Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement, type);    
+
+  // At this point, we know we have a real or pointer type. As a result, the
+  // following predicate is overkill (i.e. it will check for types we know we
+  // don't have in this context). Nevertheless, we model the C99 spec closely.
+  if (!type.isModifiableLvalue())
+    return Diag(OpLoc, diag::err_typecheck_not_modifiable, type);
+    
+  return new UnaryOperator((Expr*)Input, Opc, type);
 }
 
 Action::ExprResult Sema::
@@ -253,11 +280,8 @@
     resultType = ary->getPointeeType();
     // in practice, the following check catches trying to index a pointer
     // to a function (e.g. void (*)(int)). Functions are not objects in c99.
-    if (!resultType->isObjectType()) {
-      std::string Name;
-      baseType->getAsString(Name);
-      Diag(LLoc, diag::err_typecheck_subscript_not_object, Name);    
-    }
+    if (!resultType->isObjectType())
+      return Diag(LLoc, diag::err_typecheck_subscript_not_object, baseType);    
   } 
   return new ArraySubscriptExpr((Expr*)Base, (Expr*)Idx, resultType);
 }

Modified: cfe/cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/include/clang/AST/Expr.h?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/cfe/trunk/include/clang/AST/Expr.h Wed Jul 11 11:43:41 2007
@@ -149,8 +149,8 @@
     Extension         // __extension__ marker.
   };
 
-  UnaryOperator(Expr *input, Opcode opc)
-    : Expr(UnaryOperatorClass), Val(input), Opc(opc) {}
+  UnaryOperator(Expr *input, Opcode opc, TypeRef type)
+    : Expr(UnaryOperatorClass, type), Val(input), Opc(opc) {}
   
   /// getOpcodeStr - Turn an Opcode enum value into the punctuation char it
   /// corresponds to, e.g. "sizeof" or "[pre]++"

Modified: cfe/cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/include/clang/AST/Type.h?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/cfe/trunk/include/clang/AST/Type.h Wed Jul 11 11:43:41 2007
@@ -116,7 +116,9 @@
   /// getCanonicalType - Return the canonical version of this type, with the
   /// appropriate type qualifiers on it.
   inline TypeRef getCanonicalType() const;
-  
+
+  bool isModifiableLvalue() const;
+    
   void getAsString(std::string &S) const;
   void dump() const;
 };
@@ -201,10 +203,13 @@
   
   /// Helper methods to distinguish type categories. All type predicates
   /// operate on the canonical type, ignoring typedefs.
-  bool isIntegralType() const;   // C99 6.2.5p17 (int, char, bool, enum)
-  bool isFloatingType() const;   // C99 6.2.5p11 (float, double, long double)
-  bool isArithmeticType() const; // C99 6.2.5p18 (integral + floating)
-  bool isVoidType() const;       // C99 6.2.5p19
+  bool isIntegralType() const;     // C99 6.2.5p17 (int, char, bool, enum)
+  bool isRealFloatingType() const; // C99 6.2.5p10 (float, double, long double)
+  bool isComplexType() const;      // C99 6.2.5p11 (complex)
+  bool isFloatingType() const;     // C99 6.2.5p11 (real floating + complex)
+  bool isRealType() const;         // C99 6.2.5p17 (real floating + integer)
+  bool isArithmeticType() const;   // C99 6.2.5p18 (integral + floating)
+  bool isVoidType() const;         // C99 6.2.5p19
 
   /// Derived types (C99 6.2.5p20). isFunctionType() is also a derived type.
   bool isDerivedType() const;
@@ -215,7 +220,14 @@
   
   bool isScalarType() const;     // C99 6.2.5p21 (arithmetic + pointers)
   bool isAggregateType() const;  // C99 6.2.5p21 (arrays, structures)
-
+  
+  bool isLvalue() const;         // C99 6.3.2.1
+private:
+  // this forces clients to use isModifiableLvalue on TypeRef, the class that 
+  // knows if the type is const. This predicate is a helper to TypeRef. 
+  bool isModifiableLvalue() const; // C99 6.3.2.1
+  friend class TypeRef;
+public:
   virtual void getAsString(std::string &InnerString) const = 0;
   
   static bool classof(const Type *) { return true; }
@@ -459,6 +471,7 @@
   RecordDecl *getDecl() const {
     return reinterpret_cast<RecordDecl*>(TagType::getDecl());
   }
+  bool isModifiableLvalue() const { return true; } // FIXME
   
   static bool classof(const Type *T);
   static bool classof(const RecordType *) { return true; }

Modified: cfe/cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=39369&r1=39368&r2=39369&view=diff

==============================================================================
--- cfe/cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/cfe/trunk/include/clang/Basic/DiagnosticKinds.def Wed Jul 11 11:43:41 2007
@@ -498,6 +498,10 @@
      "incomplete definition of type '%s'")
 DIAG(err_typecheck_no_member, ERROR,
      "no member named '%s'")
+DIAG(err_typecheck_illegal_increment_decrement, ERROR,
+     "cannot modify value of type '%s'")
+DIAG(err_typecheck_not_modifiable, ERROR,
+     "cannot modify read-only value of type '%s'")
 
 // Statements.
 DIAG(err_continue_not_in_loop, ERROR,





More information about the cfe-commits mailing list