From 081c7422b24175b377083ce882da98244ea8c7e2 Mon Sep 17 00:00:00 2001 From: Steve Naroff Date: Thu, 4 Sep 2008 15:10:53 +0000 Subject: [PATCH] Add type checking for blocks. llvm-svn: 55767 --- clang/include/clang/AST/ASTContext.h | 1 + clang/include/clang/Basic/DiagnosticKinds.def | 10 ++- clang/lib/AST/ASTContext.cpp | 46 +++++++++++ clang/lib/Sema/Sema.h | 17 ++++ clang/lib/Sema/SemaExpr.cpp | 80 +++++++++++++++++++ 5 files changed, 153 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index faf4cc669715..658085907ce5 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -416,6 +416,7 @@ public: /// Compatibility predicates used to check assignment expressions. bool typesAreCompatible(QualType, QualType); // C99 6.2.7p1 + bool typesAreBlockCompatible(QualType lhs, QualType rhs); bool isObjCIdType(QualType T) const { if (!IdStructType) // ObjC isn't enabled diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 2fe462a9a0c4..d8c0ef7e6859 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -998,6 +998,14 @@ DIAG(ext_typecheck_convert_incompatible_pointer, EXTWARN, "incompatible pointer types %2 '%1', expected '%0'") DIAG(ext_typecheck_convert_discards_qualifiers, EXTWARN, "%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, "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, "expected '{' in block literal") 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, "return not allowed in block expression literal") diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2acdfaca77f8..b84b958fb030 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1767,6 +1767,52 @@ bool ASTContext::isObjCObjectPointerType(QualType Ty) const { // 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(lcanon) && isa(rcanon)) { + const FunctionType *lbase = cast(lcanon); + const FunctionType *rbase = cast(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(lbase); + const FunctionTypeProto *rproto = dyn_cast(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 /// compatible. static bool areCompatVectorTypes(const VectorType *LHS, diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index f048fd7a3a97..320f3ac2bc4a 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -816,6 +816,18 @@ private: /// CompatiblePointerDiscardsQualifiers - The assignment discards /// c/v/r qualifiers, which we accept as an extension. 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 /// represent it in the AST. @@ -849,6 +861,11 @@ private: // Helper function for CheckAssignmentConstraints (C99 6.5.16.1p1) AssignConvertType CheckPointerTypesForAssignment(QualType lhsType, 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 /// or a null QualType (indicating an error diagnostic was issued). diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 14fec20c9fe9..37172cbf0ddc 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1431,6 +1431,34 @@ Sema::CheckPointerTypesForAssignment(QualType lhsType, QualType rhsType) { 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 /// has code to accommodate several GCC extensions when type checking /// pointers. Here are some objectionable examples that GCC considers warnings: @@ -1500,6 +1528,25 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) { if (isa(rhsType)) return CheckPointerTypesForAssignment(lhsType, rhsType); + + if (const BlockPointerType *BPT = rhsType->getAsBlockPointerType()) + if (BPT->getPointeeType()->isVoidType()) + return BlockVoidPointer; + + return Incompatible; + } + + if (isa(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; } @@ -1513,6 +1560,10 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) { if (isa(lhsType)) return CheckPointerTypesForAssignment(lhsType, rhsType); + + if (isa(lhsType) && + rhsType->getAsPointerType()->getPointeeType()->isVoidType()) + return BlockVoidPointer; return Incompatible; } @@ -1532,6 +1583,11 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) { ImpCastExprToType(rExpr, lhsType); 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 // conversion of functions/arrays. If the conversion were done for all // 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 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 (ObjCQualifiedIdTypesAreCompatible(lType, rType, true)) { ImpCastExprToType(rex, lType); @@ -2875,6 +2946,15 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case CompatiblePointerDiscardsQualifiers: DiagKind = diag::ext_typecheck_convert_discards_qualifiers; 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: DiagKind = diag::err_typecheck_convert_incompatible; isInvalid = true;