From 7154a77e7c1f23418342d3b72836ab504aa7821e Mon Sep 17 00:00:00 2001 From: Steve Naroff Date: Wed, 1 Jul 2009 14:36:47 +0000 Subject: [PATCH] Rework Sema::CheckConditionalOperands(). No functionality change. This was necessary to simplify some other changes I'm making (wrt ObjC type cleanups). The idea is to separate the constraint checks for block pointers, ObjC pointers, and C pointers (the previous code combined them into one clause). Note: This routine will be further simplified when I integrate the ObjC type cleanups (forthcoming). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74604 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 1 + lib/AST/Type.cpp | 6 ++ lib/Sema/SemaExpr.cpp | 228 ++++++++++++++++++++++----------------- 3 files changed, 137 insertions(+), 98 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 82a84b3847..a00c0c4ff1 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -376,6 +376,7 @@ public: bool isFunctionProtoType() const { return getAsFunctionProtoType() != 0; } bool isPointerType() const; bool isBlockPointerType() const; + bool isVoidPointerType() const; bool isReferenceType() const; bool isLValueReferenceType() const; bool isRValueReferenceType() const; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index eaa7d3b0d8..4e061a9fbe 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -185,6 +185,12 @@ bool Type::isStructureType() const { return RT->getDecl()->isStruct(); return false; } +bool Type::isVoidPointerType() const { + if (const PointerType *PT = getAsPointerType()) + return PT->getPointeeType()->isVoidType(); + return false; +} + bool Type::isUnionType() const { if (const RecordType *RT = getAsRecordType()) return RT->getDecl()->isUnion(); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e9c88d7f6e..a5e5083964 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3079,24 +3079,133 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, ImpCastExprToType(LHS, RHSTy); // promote the null to a pointer. return RHSTy; } + // Handle block pointer types. + if (LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) { + if (!LHSTy->isBlockPointerType() || !RHSTy->isBlockPointerType()) { + if (LHSTy->isVoidPointerType() || RHSTy->isVoidPointerType()) { + QualType destType = Context.getPointerType(Context.VoidTy); + ImpCastExprToType(LHS, destType); + ImpCastExprToType(RHS, destType); + return destType; + } + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); + return QualType(); + } + // We have 2 block pointer types. + if (Context.getCanonicalType(LHSTy) == Context.getCanonicalType(RHSTy)) { + // Two identical block pointer types are always compatible. + return LHSTy; + } + // The block pointer types aren't identical, continue checking. + QualType lhptee = LHSTy->getAsBlockPointerType()->getPointeeType(); + QualType rhptee = RHSTy->getAsBlockPointerType()->getPointeeType(); - const PointerType *LHSPT = LHSTy->getAsPointerType(); - const PointerType *RHSPT = RHSTy->getAsPointerType(); - const BlockPointerType *LHSBPT = LHSTy->getAsBlockPointerType(); - const BlockPointerType *RHSBPT = RHSTy->getAsBlockPointerType(); + if (!Context.typesAreCompatible(lhptee.getUnqualifiedType(), + rhptee.getUnqualifiedType())) { + Diag(QuestionLoc, diag::warn_typecheck_cond_incompatible_pointers) + << LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); + // In this situation, we assume void* type. No especially good + // reason, but this is what gcc does, and we do have to pick + // to get a consistent AST. + QualType incompatTy = Context.getPointerType(Context.VoidTy); + ImpCastExprToType(LHS, incompatTy); + ImpCastExprToType(RHS, incompatTy); + return incompatTy; + } + // The block pointer types are compatible. + ImpCastExprToType(LHS, LHSTy); + ImpCastExprToType(RHS, LHSTy); + return LHSTy; + } + // Need to handle "id" explicitly. Unlike "id", whose canonical type + // evaluates to "struct objc_object *" (and is handled above when comparing + // id with statically typed objects). + if (LHSTy->isObjCQualifiedIdType() || RHSTy->isObjCQualifiedIdType()) { + // GCC allows qualified id and any Objective-C type to devolve to + // id. Currently localizing to here until clear this should be + // part of ObjCQualifiedIdTypesAreCompatible. + if (ObjCQualifiedIdTypesAreCompatible(LHSTy, RHSTy, true) || + (LHSTy->isObjCQualifiedIdType() && + Context.isObjCObjectPointerType(RHSTy)) || + (RHSTy->isObjCQualifiedIdType() && + Context.isObjCObjectPointerType(LHSTy))) { + // FIXME: This is not the correct composite type. This only happens to + // work because id can more or less be used anywhere, however this may + // change the type of method sends. - // Handle the case where both operands are pointers before we handle null - // pointer constants in case both operands are null pointer constants. - if ((LHSPT || LHSBPT) && (RHSPT || RHSBPT)) { // C99 6.5.15p3,6 + // FIXME: gcc adds some type-checking of the arguments and emits + // (confusing) incompatible comparison warnings in some + // cases. Investigate. + QualType compositeType = Context.getObjCIdType(); + ImpCastExprToType(LHS, compositeType); + ImpCastExprToType(RHS, compositeType); + return compositeType; + } + } + // Check constraints for Objective-C object pointers types. + if (Context.isObjCObjectPointerType(LHSTy) && + Context.isObjCObjectPointerType(RHSTy)) { + + if (Context.getCanonicalType(LHSTy) == Context.getCanonicalType(RHSTy)) { + // Two identical object pointer types are always compatible. + return LHSTy; + } + // No need to check for block pointer types or qualified id types (they + // were handled above). + assert((LHSTy->isPointerType() && RHSTy->isPointerType()) && + "Sema::CheckConditionalOperands(): Unexpected type"); + QualType lhptee = LHSTy->getAsPointerType()->getPointeeType(); + QualType rhptee = RHSTy->getAsPointerType()->getPointeeType(); + + QualType compositeType = LHSTy; + + // If both operands are interfaces and either operand can be + // assigned to the other, use that type as the composite + // type. This allows + // xxx ? (A*) a : (B*) b + // where B is a subclass of A. + // + // Additionally, as for assignment, if either type is 'id' + // allow silent coercion. Finally, if the types are + // incompatible then make sure to use 'id' as the composite + // type so the result is acceptable for sending messages to. + + // FIXME: Consider unifying with 'areComparableObjCPointerTypes'. + // It could return the composite type. + const ObjCInterfaceType* LHSIface = lhptee->getAsObjCInterfaceType(); + const ObjCInterfaceType* RHSIface = rhptee->getAsObjCInterfaceType(); + if (LHSIface && RHSIface && + Context.canAssignObjCInterfaces(LHSIface, RHSIface)) { + compositeType = LHSTy; + } else if (LHSIface && RHSIface && + Context.canAssignObjCInterfaces(RHSIface, LHSIface)) { + compositeType = RHSTy; + } else if (Context.isObjCIdStructType(lhptee) || + Context.isObjCIdStructType(rhptee)) { + compositeType = Context.getObjCIdType(); + } else { + Diag(QuestionLoc, diag::ext_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy + << LHS->getSourceRange() << RHS->getSourceRange(); + QualType incompatTy = Context.getObjCIdType(); + ImpCastExprToType(LHS, incompatTy); + ImpCastExprToType(RHS, incompatTy); + return incompatTy; + } + // The object pointer types are compatible. + ImpCastExprToType(LHS, compositeType); + ImpCastExprToType(RHS, compositeType); + return compositeType; + } + // Check constraints for C object pointers types (C99 6.5.15p3,6). + if (LHSTy->isPointerType() && RHSTy->isPointerType()) { // get the "pointed to" types - QualType lhptee = (LHSPT ? LHSPT->getPointeeType() - : LHSBPT->getPointeeType()); - QualType rhptee = (RHSPT ? RHSPT->getPointeeType() - : RHSBPT->getPointeeType()); + QualType lhptee = LHSTy->getAsPointerType()->getPointeeType(); + QualType rhptee = RHSTy->getAsPointerType()->getPointeeType(); // ignore qualifiers on void (C99 6.5.15p3, clause 6) - if (lhptee->isVoidType() - && (RHSBPT || rhptee->isIncompleteOrObjectType())) { + if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { // Figure out necessary qualifiers (C99 6.5.15p6) QualType destPointee=lhptee.getQualifiedType(rhptee.getCVRQualifiers()); QualType destType = Context.getPointerType(destPointee); @@ -3104,8 +3213,7 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, ImpCastExprToType(RHS, destType); // promote to void* return destType; } - if (rhptee->isVoidType() - && (LHSBPT || lhptee->isIncompleteOrObjectType())) { + if (rhptee->isVoidType() && lhptee->isIncompleteOrObjectType()) { QualType destPointee=rhptee.getQualifiedType(lhptee.getCVRQualifiers()); QualType destType = Context.getPointerType(destPointee); ImpCastExprToType(LHS, destType); // add qualifiers if necessary @@ -3113,62 +3221,12 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, return destType; } - bool sameKind = (LHSPT && RHSPT) || (LHSBPT && RHSBPT); - if (sameKind - && Context.getCanonicalType(LHSTy) == Context.getCanonicalType(RHSTy)) { + if (Context.getCanonicalType(LHSTy) == Context.getCanonicalType(RHSTy)) { // Two identical pointer types are always compatible. return LHSTy; } - - QualType compositeType = LHSTy; - - // If either type is an Objective-C object type then check - // compatibility according to Objective-C. - if (Context.isObjCObjectPointerType(LHSTy) || - Context.isObjCObjectPointerType(RHSTy)) { - // If both operands are interfaces and either operand can be - // assigned to the other, use that type as the composite - // type. This allows - // xxx ? (A*) a : (B*) b - // where B is a subclass of A. - // - // Additionally, as for assignment, if either type is 'id' - // allow silent coercion. Finally, if the types are - // incompatible then make sure to use 'id' as the composite - // type so the result is acceptable for sending messages to. - - // FIXME: Consider unifying with 'areComparableObjCPointerTypes'. - // It could return the composite type. - const ObjCInterfaceType* LHSIface = lhptee->getAsObjCInterfaceType(); - const ObjCInterfaceType* RHSIface = rhptee->getAsObjCInterfaceType(); - if (LHSIface && RHSIface && - Context.canAssignObjCInterfaces(LHSIface, RHSIface)) { - compositeType = LHSTy; - } else if (LHSIface && RHSIface && - Context.canAssignObjCInterfaces(RHSIface, LHSIface)) { - compositeType = RHSTy; - } else if (Context.isObjCIdStructType(lhptee) || - Context.isObjCIdStructType(rhptee)) { - compositeType = Context.getObjCIdType(); - } else if (LHSBPT || RHSBPT) { - if (!sameKind - || !Context.typesAreCompatible(lhptee.getUnqualifiedType(), - rhptee.getUnqualifiedType())) - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); - return QualType(); - } else { - Diag(QuestionLoc, diag::ext_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy - << LHS->getSourceRange() << RHS->getSourceRange(); - QualType incompatTy = Context.getObjCIdType(); - ImpCastExprToType(LHS, incompatTy); - ImpCastExprToType(RHS, incompatTy); - return incompatTy; - } - } else if (!sameKind - || !Context.typesAreCompatible(lhptee.getUnqualifiedType(), - rhptee.getUnqualifiedType())) { + if (!Context.typesAreCompatible(lhptee.getUnqualifiedType(), + rhptee.getUnqualifiedType())) { Diag(QuestionLoc, diag::warn_typecheck_cond_incompatible_pointers) << LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); // In this situation, we assume void* type. No especially good @@ -3186,11 +3244,11 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, // type. // FIXME: Need to calculate the composite type. // FIXME: Need to add qualifiers - ImpCastExprToType(LHS, compositeType); - ImpCastExprToType(RHS, compositeType); - return compositeType; + ImpCastExprToType(LHS, LHSTy); + ImpCastExprToType(RHS, LHSTy); + return LHSTy; } - + // GCC compatibility: soften pointer/integer mismatch. if (RHSTy->isPointerType() && LHSTy->isIntegerType()) { Diag(QuestionLoc, diag::warn_typecheck_cond_pointer_integer_mismatch) @@ -3205,32 +3263,6 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, return LHSTy; } - // Need to handle "id" explicitly. Unlike "id", whose canonical type - // evaluates to "struct objc_object *" (and is handled above when comparing - // id with statically typed objects). - if (LHSTy->isObjCQualifiedIdType() || RHSTy->isObjCQualifiedIdType()) { - // GCC allows qualified id and any Objective-C type to devolve to - // id. Currently localizing to here until clear this should be - // part of ObjCQualifiedIdTypesAreCompatible. - if (ObjCQualifiedIdTypesAreCompatible(LHSTy, RHSTy, true) || - (LHSTy->isObjCQualifiedIdType() && - Context.isObjCObjectPointerType(RHSTy)) || - (RHSTy->isObjCQualifiedIdType() && - Context.isObjCObjectPointerType(LHSTy))) { - // FIXME: This is not the correct composite type. This only happens to - // work because id can more or less be used anywhere, however this may - // change the type of method sends. - - // FIXME: gcc adds some type-checking of the arguments and emits - // (confusing) incompatible comparison warnings in some - // cases. Investigate. - QualType compositeType = Context.getObjCIdType(); - ImpCastExprToType(LHS, compositeType); - ImpCastExprToType(RHS, compositeType); - return compositeType; - } - } - // Otherwise, the operands are not compatible. Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) << LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange(); -- 2.40.0