[cfe-commits] r51318 - in /cfe/trunk/lib/Sema: Sema.h SemaDecl.cpp

Eli Friedman eli.friedman at gmail.com
Tue May 20 06:48:25 PDT 2008


Author: efriedma
Date: Tue May 20 08:48:25 2008
New Revision: 51318

URL: http://llvm.org/viewvc/llvm-project?rev=51318&view=rev
Log:
Perform stricter/more accurate checking for C99 constant expressions
in Sema, per discussion on mailing list.  This doesn't cause any changes 
in the test results.  I'll probably add some more tests at some point, 
but it's an open question what we need to accept to be compatible with 
real code.

This doesn't touch the existing isConstantExpr method on Expr; that 
should be addressed somehow eventually (either removed or refined to 
whatever is appropriate).


Modified:
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue May 20 08:48:25 2008
@@ -857,6 +857,9 @@
   bool CheckInitializerListTypes(InitListExpr*& IList, QualType &DeclType,
                                  bool topLevel, unsigned& startIndex);
   bool CheckForConstantInitializer(Expr *e, QualType t);
+  bool CheckArithmeticConstantExpression(const Expr* e);
+  bool CheckAddressConstantExpression(const Expr* e);
+  bool CheckAddressConstantExpressionLValue(const Expr* e);
   
   StringLiteral *IsStringLiteralInit(Expr *Init, QualType DeclType);
   bool CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT);

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=51318&r1=51317&r2=51318&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue May 20 08:48:25 2008
@@ -24,6 +24,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/AST/ExprCXX.h"
 // FIXME: layering (ideally, Sema shouldn't be dependent on Lex API's)
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/HeaderSearch.h" 
@@ -989,15 +990,351 @@
   return New;
 }
 
-bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) {
-  SourceLocation loc;
-  // FIXME: Remove the isReference check and handle assignment to a reference.
-  if (!DclT->isReferenceType() && !Init->isConstantExpr(Context, &loc)) { 
-    assert(loc.isValid() && "isConstantExpr didn't return a loc!");
-    Diag(loc, diag::err_init_element_not_constant, Init->getSourceRange());
+bool Sema::CheckAddressConstantExpressionLValue(const Expr* Init) {
+  switch (Init->getStmtClass()) {
+  default:
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  case Expr::ParenExprClass: {
+    const ParenExpr* PE = cast<ParenExpr>(Init);
+    return CheckAddressConstantExpressionLValue(PE->getSubExpr());
+  }
+  case Expr::CompoundLiteralExprClass:
+    return cast<CompoundLiteralExpr>(Init)->isFileScope();
+  case Expr::DeclRefExprClass: {
+    const Decl *D = cast<DeclRefExpr>(Init)->getDecl();
+    if (const VarDecl *VD = dyn_cast<VarDecl>(D))
+      return VD->hasGlobalStorage();
+    if (isa<FunctionDecl>(D))
+      return false;
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
     return true;
   }
-  return false;
+  case Expr::MemberExprClass: {
+    const MemberExpr *M = cast<MemberExpr>(Init);
+    if (M->isArrow())
+      return CheckAddressConstantExpression(M->getBase());
+    return CheckAddressConstantExpressionLValue(M->getBase());
+  }
+  case Expr::ArraySubscriptExprClass: {
+    // FIXME: Should we pedwarn for "x[0+0]" (where x is a pointer)?
+    const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(Init);
+    return CheckAddressConstantExpression(ASE->getBase()) ||
+           CheckArithmeticConstantExpression(ASE->getIdx());
+  }
+  case Expr::StringLiteralClass:
+  case Expr::PreDefinedExprClass:
+    return false;
+  case Expr::UnaryOperatorClass: {
+    const UnaryOperator *Exp = cast<UnaryOperator>(Init);
+
+    // C99 6.6p9
+    if (Exp->getOpcode() == UnaryOperator::Deref)
+      return CheckAddressConstantExpressionLValue(Exp->getSubExpr());
+
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  }
+}
+
+bool Sema::CheckAddressConstantExpression(const Expr* Init) {
+  switch (Init->getStmtClass()) {
+  default:
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  case Expr::ParenExprClass: {
+    const ParenExpr* PE = cast<ParenExpr>(Init);
+    return CheckAddressConstantExpression(PE->getSubExpr());
+  }
+  case Expr::StringLiteralClass:
+  case Expr::ObjCStringLiteralClass:
+    return false;
+  case Expr::CallExprClass: {
+    const CallExpr *CE = cast<CallExpr>(Init);
+    if (CE->isBuiltinConstantExpr())
+      return false;
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::UnaryOperatorClass: {
+    const UnaryOperator *Exp = cast<UnaryOperator>(Init);
+
+    // C99 6.6p9
+    if (Exp->getOpcode() == UnaryOperator::AddrOf)
+      return CheckAddressConstantExpressionLValue(Exp->getSubExpr());
+
+    if (Exp->getOpcode() == UnaryOperator::Extension)
+      return CheckAddressConstantExpression(Exp->getSubExpr());
+  
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::BinaryOperatorClass: {
+    // FIXME: Should we pedwarn for expressions like "a + 1 + 2"?
+    const BinaryOperator *Exp = cast<BinaryOperator>(Init);
+
+    Expr *PExp = Exp->getLHS();
+    Expr *IExp = Exp->getRHS();
+    if (IExp->getType()->isPointerType())
+      std::swap(PExp, IExp);
+
+    // FIXME: Should we pedwarn if IExp isn't an integer constant expression?
+    return CheckAddressConstantExpression(PExp) ||
+           CheckArithmeticConstantExpression(IExp);
+  }
+  case Expr::ImplicitCastExprClass: {
+    const Expr* SubExpr = cast<ImplicitCastExpr>(Init)->getSubExpr();
+
+    // Check for implicit promotion
+    if (SubExpr->getType()->isFunctionType() ||
+        SubExpr->getType()->isArrayType())
+      return CheckAddressConstantExpressionLValue(SubExpr);
+
+    // Check for pointer->pointer cast
+    if (SubExpr->getType()->isPointerType())
+      return CheckAddressConstantExpression(SubExpr);
+
+    if (SubExpr->getType()->isArithmeticType())
+      return CheckArithmeticConstantExpression(SubExpr);
+
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::CastExprClass: {
+    const Expr* SubExpr = cast<CastExpr>(Init)->getSubExpr();
+
+    // Check for pointer->pointer cast
+    if (SubExpr->getType()->isPointerType())
+      return CheckAddressConstantExpression(SubExpr);
+
+    // FIXME: Should we pedwarn for (int*)(0+0)?
+    if (SubExpr->getType()->isArithmeticType())
+      return CheckArithmeticConstantExpression(SubExpr);
+
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::ConditionalOperatorClass: {
+    // FIXME: Should we pedwarn here?
+    const ConditionalOperator *Exp = cast<ConditionalOperator>(Init);
+    if (!Exp->getCond()->getType()->isArithmeticType()) {
+      Diag(Init->getExprLoc(),
+           diag::err_init_element_not_constant, Init->getSourceRange());
+      return true;
+    }
+    if (CheckArithmeticConstantExpression(Exp->getCond()))
+      return true;
+    if (Exp->getLHS() &&
+        CheckAddressConstantExpression(Exp->getLHS()))
+      return true;
+    return CheckAddressConstantExpression(Exp->getRHS());
+  }
+  case Expr::AddrLabelExprClass:
+    return false;
+  }
+}
+
+bool Sema::CheckArithmeticConstantExpression(const Expr* Init) {
+  switch (Init->getStmtClass()) {
+  default:
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  case Expr::ParenExprClass: {
+    const ParenExpr* PE = cast<ParenExpr>(Init);
+    return CheckArithmeticConstantExpression(PE->getSubExpr());
+  }
+  case Expr::FloatingLiteralClass:
+  case Expr::IntegerLiteralClass:
+  case Expr::CharacterLiteralClass:
+  case Expr::ImaginaryLiteralClass:
+  case Expr::TypesCompatibleExprClass:
+  case Expr::CXXBoolLiteralExprClass:
+    return false;
+  case Expr::CallExprClass: {
+    const CallExpr *CE = cast<CallExpr>(Init);
+    if (CE->isBuiltinConstantExpr())
+      return false;
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::DeclRefExprClass: {
+    const Decl *D = cast<DeclRefExpr>(Init)->getDecl();
+    if (isa<EnumConstantDecl>(D))
+      return false;
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::CompoundLiteralExprClass:
+    // Allow "(vector type){2,4}"; normal C constraints don't allow this,
+    // but vectors are allowed to be magic.
+    if (Init->getType()->isVectorType())
+      return false;
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  case Expr::UnaryOperatorClass: {
+    const UnaryOperator *Exp = cast<UnaryOperator>(Init);
+  
+    switch (Exp->getOpcode()) {
+    // Address, indirect, pre/post inc/dec, etc are not valid constant exprs.
+    // See C99 6.6p3.
+    default:
+      Diag(Init->getExprLoc(),
+           diag::err_init_element_not_constant, Init->getSourceRange());
+      return true;
+    case UnaryOperator::SizeOf:
+    case UnaryOperator::AlignOf:
+    case UnaryOperator::OffsetOf:
+      // sizeof(E) is a constantexpr if and only if E is not evaluted.
+      // See C99 6.5.3.4p2 and 6.6p3.
+      if (Exp->getSubExpr()->getType()->isConstantSizeType())
+        return false;
+      Diag(Init->getExprLoc(),
+           diag::err_init_element_not_constant, Init->getSourceRange());
+      return true;
+    case UnaryOperator::Extension:
+    case UnaryOperator::LNot:
+    case UnaryOperator::Plus:
+    case UnaryOperator::Minus:
+    case UnaryOperator::Not:
+      return CheckArithmeticConstantExpression(Exp->getSubExpr());
+    }
+  }
+  case Expr::SizeOfAlignOfTypeExprClass: {
+    const SizeOfAlignOfTypeExpr *Exp = cast<SizeOfAlignOfTypeExpr>(Init);
+    // Special check for void types, which are allowed as an extension
+    if (Exp->getArgumentType()->isVoidType())
+      return false;
+    // alignof always evaluates to a constant.
+    // FIXME: is sizeof(int[3.0]) a constant expression?
+    if (Exp->isSizeOf() && !Exp->getArgumentType()->isConstantSizeType()) {
+      Diag(Init->getExprLoc(),
+           diag::err_init_element_not_constant, Init->getSourceRange());
+      return true;
+    }
+    return false;
+  }
+  case Expr::BinaryOperatorClass: {
+    const BinaryOperator *Exp = cast<BinaryOperator>(Init);
+
+    if (Exp->getLHS()->getType()->isArithmeticType() &&
+        Exp->getRHS()->getType()->isArithmeticType()) {
+      return CheckArithmeticConstantExpression(Exp->getLHS()) ||
+             CheckArithmeticConstantExpression(Exp->getRHS());
+    }
+
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::ImplicitCastExprClass:
+  case Expr::CastExprClass: {
+    const Expr *SubExpr;
+    if (const CastExpr *C = dyn_cast<CastExpr>(Init)) {
+      SubExpr = C->getSubExpr();
+    } else {
+      SubExpr = cast<ImplicitCastExpr>(Init)->getSubExpr();
+    }
+
+    if (SubExpr->getType()->isArithmeticType())
+      return CheckArithmeticConstantExpression(SubExpr);
+
+    Diag(Init->getExprLoc(),
+         diag::err_init_element_not_constant, Init->getSourceRange());
+    return true;
+  }
+  case Expr::ConditionalOperatorClass: {
+    const ConditionalOperator *Exp = cast<ConditionalOperator>(Init);
+    if (CheckArithmeticConstantExpression(Exp->getCond()))
+      return true;
+    if (Exp->getLHS() &&
+        CheckArithmeticConstantExpression(Exp->getLHS()))
+      return true;
+    return CheckArithmeticConstantExpression(Exp->getRHS());
+  }
+  }
+}
+
+bool Sema::CheckForConstantInitializer(Expr *Init, QualType DclT) {
+  // Look through CXXDefaultArgExprs; they have no meaning in this context.
+  if (CXXDefaultArgExpr* DAE = dyn_cast<CXXDefaultArgExpr>(Init))
+    return CheckForConstantInitializer(DAE->getExpr(), DclT);
+
+  if (Init->getType()->isReferenceType()) {
+    // FIXME: Work out how the heck reference types work
+    return false;
+#if 0
+    // A reference is constant if the address of the expression
+    // is constant
+    // We look through initlists here to simplify
+    // CheckAddressConstantExpressionLValue.
+    if (InitListExpr *Exp = dyn_cast<InitListExpr>(Init)) {
+      assert(Exp->getNumInits() > 0 &&
+             "Refernce initializer cannot be empty");
+      Init = Exp->getInit(0);
+    }
+    return CheckAddressConstantExpressionLValue(Init);
+#endif
+  }
+
+  if (InitListExpr *Exp = dyn_cast<InitListExpr>(Init)) {
+    unsigned numInits = Exp->getNumInits();
+    for (unsigned i = 0; i < numInits; i++) {
+      // FIXME: Need to get the type of the declaration for C++,
+      // because it could be a reference?
+      if (CheckForConstantInitializer(Exp->getInit(i),
+                                      Exp->getInit(i)->getType()))
+        return true;
+    }
+    return false;
+  }
+
+  if (Init->isNullPointerConstant(Context))
+    return false;
+  if (Init->getType()->isArithmeticType()) {
+    // Special check for pointer cast to int; we allow
+    // an address constant cast to an integer if the integer
+    // is of an appropriate width (this sort of code is apparently used
+    // in some places).
+    // FIXME: Add pedwarn?
+    Expr* SubE;
+    if (ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(Init))
+      SubE = ICE->getSubExpr();
+    else if (CastExpr* CE = dyn_cast<CastExpr>(Init))
+      SubE = CE->getSubExpr();
+    if (SubE && (SubE->getType()->isPointerType() ||
+                 SubE->getType()->isArrayType() ||
+                 SubE->getType()->isFunctionType())) {
+      unsigned IntWidth = Context.getTypeSize(Init->getType());
+      unsigned PointerWidth = Context.getTypeSize(Context.VoidPtrTy);
+      if (IntWidth >= PointerWidth)
+        return CheckAddressConstantExpression(Init);
+    }
+
+    return CheckArithmeticConstantExpression(Init);
+  }
+
+  if (Init->getType()->isPointerType())
+    return CheckAddressConstantExpression(Init);
+
+  if (Init->getType()->isArrayType())
+    return false;
+
+  Diag(Init->getExprLoc(), diag::err_init_element_not_constant,
+       Init->getSourceRange());
+  return true;
 }
 
 void Sema::AddInitializerToDecl(DeclTy *dcl, ExprTy *init) {





More information about the cfe-commits mailing list