Add type checking for blocks.

llvm-svn: 55767
This commit is contained in:
Steve Naroff 2008-09-04 15:10:53 +00:00
parent 95d573a7f0
commit 081c7422b2
5 changed files with 153 additions and 1 deletions

View File

@ -416,6 +416,7 @@ public:
/// Compatibility predicates used to check assignment expressions. /// Compatibility predicates used to check assignment expressions.
bool typesAreCompatible(QualType, QualType); // C99 6.2.7p1 bool typesAreCompatible(QualType, QualType); // C99 6.2.7p1
bool typesAreBlockCompatible(QualType lhs, QualType rhs);
bool isObjCIdType(QualType T) const { bool isObjCIdType(QualType T) const {
if (!IdStructType) // ObjC isn't enabled if (!IdStructType) // ObjC isn't enabled

View File

@ -998,6 +998,14 @@ DIAG(ext_typecheck_convert_incompatible_pointer, EXTWARN,
"incompatible pointer types %2 '%1', expected '%0'") "incompatible pointer types %2 '%1', expected '%0'")
DIAG(ext_typecheck_convert_discards_qualifiers, EXTWARN, DIAG(ext_typecheck_convert_discards_qualifiers, EXTWARN,
"%2 '%1' discards qualifiers, expected '%0'") "%2 '%1' discards qualifiers, expected '%0'")
DIAG(err_int_to_block_pointer, ERROR,
"invalid conversion %2 integer '%1', expected block pointer '%0'")
DIAG(err_typecheck_comparison_of_distinct_blocks, ERROR,
"comparison of distinct block types ('%0' and '%1')")
DIAG(err_typecheck_convert_incompatible_block_pointer, ERROR,
"incompatible block pointer types %2 '%1', expected '%0'")
DIAG(ext_typecheck_convert_pointer_void_block, EXTENSION,
"%2 '%1' converts between void* and block pointer, expected '%0'")
DIAG(err_typecheck_array_not_modifiable_lvalue, ERROR, DIAG(err_typecheck_array_not_modifiable_lvalue, ERROR,
"array type '%0' is not assignable") "array type '%0' is not assignable")
@ -1133,7 +1141,7 @@ DIAG(warn_uninit_val, WARNING, "use of uninitialized variable")
DIAG(err_expected_block_lbrace, ERROR, DIAG(err_expected_block_lbrace, ERROR,
"expected '{' in block literal") "expected '{' in block literal")
DIAG(err_goto_in_block, ERROR, DIAG(err_goto_in_block, ERROR,
"goto not allowed in closure literal") "goto not allowed in block literal")
DIAG(err_return_in_block_expression, ERROR, DIAG(err_return_in_block_expression, ERROR,
"return not allowed in block expression literal") "return not allowed in block expression literal")

View File

@ -1767,6 +1767,52 @@ bool ASTContext::isObjCObjectPointerType(QualType Ty) const {
// Type Compatibility Testing // Type Compatibility Testing
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// typesAreBlockCompatible - This routine is called when comparing two
/// closure types. Types must be strictly compatible here.
bool ASTContext::typesAreBlockCompatible(QualType lhs, QualType rhs) {
if (lhs.getCVRQualifiers() != rhs.getCVRQualifiers())
return false;
QualType lcanon = getCanonicalType(lhs);
QualType rcanon = getCanonicalType(rhs);
// If two types are identical, they are are compatible
if (lcanon == rcanon)
return true;
if (isa<FunctionType>(lcanon) && isa<FunctionType>(rcanon)) {
const FunctionType *lbase = cast<FunctionType>(lcanon);
const FunctionType *rbase = cast<FunctionType>(rcanon);
// First check the return types.
if (!typesAreBlockCompatible(lbase->getResultType(),rbase->getResultType()))
return false;
// Return types matched, now check the argument types.
const FunctionTypeProto *lproto = dyn_cast<FunctionTypeProto>(lbase);
const FunctionTypeProto *rproto = dyn_cast<FunctionTypeProto>(rbase);
if (lproto && rproto) { // two C99 style function prototypes
unsigned lproto_nargs = lproto->getNumArgs();
unsigned rproto_nargs = rproto->getNumArgs();
if (lproto_nargs != rproto_nargs)
return false;
if (lproto->isVariadic() || rproto->isVariadic())
return false;
// The use of ellipsis agree...now check the argument types.
for (unsigned i = 0; i < lproto_nargs; i++)
if (!typesAreBlockCompatible(lproto->getArgType(i),
rproto->getArgType(i)))
return false;
return true;
}
return (!lproto && !rproto); // two K&R style function decls match.
}
return false;
}
/// areCompatVectorTypes - Return true if the two specified vector types are /// areCompatVectorTypes - Return true if the two specified vector types are
/// compatible. /// compatible.
static bool areCompatVectorTypes(const VectorType *LHS, static bool areCompatVectorTypes(const VectorType *LHS,

View File

@ -816,6 +816,18 @@ private:
/// CompatiblePointerDiscardsQualifiers - The assignment discards /// CompatiblePointerDiscardsQualifiers - The assignment discards
/// c/v/r qualifiers, which we accept as an extension. /// c/v/r qualifiers, which we accept as an extension.
CompatiblePointerDiscardsQualifiers, CompatiblePointerDiscardsQualifiers,
/// IntToBlockPointer - The assignment converts an int to a closure
/// pointer. We disallow this.
IntToBlockPointer,
/// IncompatibleBlockPointer - The assignment is between two closure
/// pointers types that are not compatible.
IncompatibleBlockPointer,
/// BlockVoidPointer - The assignment is between a closure pointer and
/// void*, we accept for now.
BlockVoidPointer,
/// Incompatible - We reject this conversion outright, it is invalid to /// Incompatible - We reject this conversion outright, it is invalid to
/// represent it in the AST. /// represent it in the AST.
@ -849,6 +861,11 @@ private:
// Helper function for CheckAssignmentConstraints (C99 6.5.16.1p1) // Helper function for CheckAssignmentConstraints (C99 6.5.16.1p1)
AssignConvertType CheckPointerTypesForAssignment(QualType lhsType, AssignConvertType CheckPointerTypesForAssignment(QualType lhsType,
QualType rhsType); QualType rhsType);
// Helper function for CheckAssignmentConstraints involving two
// blcok pointer types.
AssignConvertType CheckBlockPointerTypesForAssignment(QualType lhsType,
QualType rhsType);
/// the following "Check" methods will return a valid/converted QualType /// the following "Check" methods will return a valid/converted QualType
/// or a null QualType (indicating an error diagnostic was issued). /// or a null QualType (indicating an error diagnostic was issued).

View File

@ -1431,6 +1431,34 @@ Sema::CheckPointerTypesForAssignment(QualType lhsType, QualType rhsType) {
return ConvTy; return ConvTy;
} }
/// CheckBlockPointerTypesForAssignment - This routine determines whether two
/// block pointer types are compatible or whether a block and normal pointer
/// are compatible. It is more restrict than comparing two function pointer
// types.
Sema::AssignConvertType
Sema::CheckBlockPointerTypesForAssignment(QualType lhsType,
QualType rhsType) {
QualType lhptee, rhptee;
// get the "pointed to" type (ignoring qualifiers at the top level)
lhptee = lhsType->getAsBlockPointerType()->getPointeeType();
rhptee = rhsType->getAsBlockPointerType()->getPointeeType();
// make sure we operate on the canonical type
lhptee = Context.getCanonicalType(lhptee);
rhptee = Context.getCanonicalType(rhptee);
AssignConvertType ConvTy = Compatible;
// For blocks we enforce that qualifiers are identical.
if (lhptee.getCVRQualifiers() != rhptee.getCVRQualifiers())
ConvTy = CompatiblePointerDiscardsQualifiers;
if (!Context.typesAreBlockCompatible(lhptee, rhptee))
return IncompatibleBlockPointer;
return ConvTy;
}
/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently /// CheckAssignmentConstraints (C99 6.5.16) - This routine currently
/// has code to accommodate several GCC extensions when type checking /// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings: /// pointers. Here are some objectionable examples that GCC considers warnings:
@ -1500,6 +1528,25 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
if (isa<PointerType>(rhsType)) if (isa<PointerType>(rhsType))
return CheckPointerTypesForAssignment(lhsType, rhsType); return CheckPointerTypesForAssignment(lhsType, rhsType);
if (const BlockPointerType *BPT = rhsType->getAsBlockPointerType())
if (BPT->getPointeeType()->isVoidType())
return BlockVoidPointer;
return Incompatible;
}
if (isa<BlockPointerType>(lhsType)) {
if (rhsType->isIntegerType())
return IntToPointer;
if (rhsType->isBlockPointerType())
return CheckBlockPointerTypesForAssignment(lhsType, rhsType);
if (const PointerType *RHSPT = rhsType->getAsPointerType()) {
if (RHSPT->getPointeeType()->isVoidType())
return BlockVoidPointer;
}
return Incompatible; return Incompatible;
} }
@ -1513,6 +1560,10 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
if (isa<PointerType>(lhsType)) if (isa<PointerType>(lhsType))
return CheckPointerTypesForAssignment(lhsType, rhsType); return CheckPointerTypesForAssignment(lhsType, rhsType);
if (isa<BlockPointerType>(lhsType) &&
rhsType->getAsPointerType()->getPointeeType()->isVoidType())
return BlockVoidPointer;
return Incompatible; return Incompatible;
} }
@ -1532,6 +1583,11 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
ImpCastExprToType(rExpr, lhsType); ImpCastExprToType(rExpr, lhsType);
return Compatible; return Compatible;
} }
// We don't allow conversion of non-null-pointer constants to integers.
if (lhsType->isBlockPointerType() && rExpr->getType()->isIntegerType())
return IntToBlockPointer;
// This check seems unnatural, however it is necessary to ensure the proper // This check seems unnatural, however it is necessary to ensure the proper
// conversion of functions/arrays. If the conversion were done for all // conversion of functions/arrays. If the conversion were done for all
// DeclExpr's (created by ActOnIdentifierExpr), it would mess up the unary // DeclExpr's (created by ActOnIdentifierExpr), it would mess up the unary
@ -1849,6 +1905,21 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation loc,
ImpCastExprToType(rex, lType); // promote the pointer to pointer ImpCastExprToType(rex, lType); // promote the pointer to pointer
return Context.IntTy; return Context.IntTy;
} }
// Handle block pointer types.
if (lType->isBlockPointerType() && rType->isBlockPointerType()) {
QualType lpointee = lType->getAsBlockPointerType()->getPointeeType();
QualType rpointee = rType->getAsBlockPointerType()->getPointeeType();
if (!LHSIsNull && !RHSIsNull &&
!Context.typesAreBlockCompatible(lpointee, rpointee)) {
Diag(loc, diag::err_typecheck_comparison_of_distinct_blocks,
lType.getAsString(), rType.getAsString(),
lex->getSourceRange(), rex->getSourceRange());
}
ImpCastExprToType(rex, lType); // promote the pointer to pointer
return Context.IntTy;
}
if ((lType->isObjCQualifiedIdType() || rType->isObjCQualifiedIdType())) { if ((lType->isObjCQualifiedIdType() || rType->isObjCQualifiedIdType())) {
if (ObjCQualifiedIdTypesAreCompatible(lType, rType, true)) { if (ObjCQualifiedIdTypesAreCompatible(lType, rType, true)) {
ImpCastExprToType(rex, lType); ImpCastExprToType(rex, lType);
@ -2875,6 +2946,15 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
case CompatiblePointerDiscardsQualifiers: case CompatiblePointerDiscardsQualifiers:
DiagKind = diag::ext_typecheck_convert_discards_qualifiers; DiagKind = diag::ext_typecheck_convert_discards_qualifiers;
break; break;
case IntToBlockPointer:
DiagKind = diag::err_int_to_block_pointer;
break;
case IncompatibleBlockPointer:
DiagKind = diag::err_typecheck_convert_incompatible_block_pointer;
break;
case BlockVoidPointer:
DiagKind = diag::ext_typecheck_convert_pointer_void_block;
break;
case Incompatible: case Incompatible:
DiagKind = diag::err_typecheck_convert_incompatible; DiagKind = diag::err_typecheck_convert_incompatible;
isInvalid = true; isInvalid = true;