aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaDecl.cpp
diff options
context:
space:
mode:
authorEli Friedman <eli.friedman@gmail.com>2009-02-21 00:44:51 +0000
committerEli Friedman <eli.friedman@gmail.com>2009-02-21 00:44:51 +0000
commit1ca4813ddae54deead43252fe2f2d79fa5b7ad48 (patch)
tree936373f19c2c54b2c79c012f86b2549f9ba099af /lib/Sema/SemaDecl.cpp
parent4f676edd08bf1f1281b162107424141afe143055 (diff)
Re-fix r65140 correctly.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65208 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaDecl.cpp')
-rw-r--r--lib/Sema/SemaDecl.cpp79
1 files changed, 69 insertions, 10 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index dfa1e03177..faaa529096 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1412,6 +1412,44 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
return New;
}
+/// TryToFixInvalidVariablyModifiedType - Helper method to turn variable array
+/// types into constant array types in certain situations which would otherwise
+/// be errors (for GCC compatibility).
+static QualType TryToFixInvalidVariablyModifiedType(QualType T,
+ ASTContext &Context,
+ bool &SizeIsNegative) {
+ // This method tries to turn a variable array into a constant
+ // array even when the size isn't an ICE. This is necessary
+ // for compatibility with code that depends on gcc's buggy
+ // constant expression folding, like struct {char x[(int)(char*)2];}
+ SizeIsNegative = false;
+
+ if (const PointerType* PTy = dyn_cast<PointerType>(T)) {
+ QualType Pointee = PTy->getPointeeType();
+ QualType FixedType =
+ TryToFixInvalidVariablyModifiedType(Pointee, Context, SizeIsNegative);
+ if (FixedType.isNull()) return FixedType;
+ return Context.getPointerType(FixedType);
+ }
+
+ const VariableArrayType* VLATy = dyn_cast<VariableArrayType>(T);
+ if (!VLATy) return QualType();
+
+ Expr::EvalResult EvalResult;
+ if (!VLATy->getSizeExpr() ||
+ !VLATy->getSizeExpr()->Evaluate(EvalResult, Context))
+ return QualType();
+
+ assert(EvalResult.Val.isInt() && "Size expressions must be integers!");
+ llvm::APSInt &Res = EvalResult.Val.getInt();
+ if (Res >= llvm::APSInt(Res.getBitWidth(), Res.isUnsigned()))
+ return Context.getConstantArrayType(VLATy->getElementType(),
+ Res, ArrayType::Normal, 0);
+
+ SizeIsNegative = true;
+ return QualType();
+}
+
NamedDecl*
Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
QualType R, Decl* LastDeclarator,
@@ -1444,15 +1482,25 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
}
if (S->getFnParent() == 0) {
+ QualType T = NewTD->getUnderlyingType();
// C99 6.7.7p2: If a typedef name specifies a variably modified type
// then it shall have block scope.
- if (NewTD->getUnderlyingType()->isVariablyModifiedType()) {
- if (NewTD->getUnderlyingType()->isVariableArrayType())
- Diag(D.getIdentifierLoc(), diag::err_vla_decl_in_file_scope);
- else
- Diag(D.getIdentifierLoc(), diag::err_vm_decl_in_file_scope);
-
- InvalidDecl = true;
+ if (T->isVariablyModifiedType()) {
+ bool SizeIsNegative;
+ QualType FixedTy =
+ TryToFixInvalidVariablyModifiedType(T, Context, SizeIsNegative);
+ if (!FixedTy.isNull()) {
+ Diag(D.getIdentifierLoc(), diag::warn_illegal_constant_array_size);
+ NewTD->setUnderlyingType(FixedTy);
+ } else {
+ if (SizeIsNegative)
+ Diag(D.getIdentifierLoc(), diag::err_typecheck_negative_array_size);
+ else if (T->isVariableArrayType())
+ Diag(D.getIdentifierLoc(), diag::err_vla_decl_in_file_scope);
+ else
+ Diag(D.getIdentifierLoc(), diag::err_vm_decl_in_file_scope);
+ InvalidDecl = true;
+ }
}
}
return NewTD;
@@ -3449,9 +3497,20 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
// C99 6.7.2.1p8: A member of a structure or union may have any type other
// than a variably modified type.
if (T->isVariablyModifiedType()) {
- Diag(Loc, diag::err_typecheck_field_variable_size);
- T = Context.IntTy;
- InvalidDecl = true;
+ bool SizeIsNegative;
+ QualType FixedTy = TryToFixInvalidVariablyModifiedType(T, Context,
+ SizeIsNegative);
+ if (!FixedTy.isNull()) {
+ Diag(Loc, diag::warn_illegal_constant_array_size);
+ T = FixedTy;
+ } else {
+ if (SizeIsNegative)
+ Diag(Loc, diag::err_typecheck_negative_array_size);
+ else
+ Diag(Loc, diag::err_typecheck_field_variable_size);
+ T = Context.IntTy;
+ InvalidDecl = true;
+ }
}
if (BitWidth) {