Commit 1074d9d4 by Nicola Pero Committed by Nicola Pero

Fixed ObjC typechecking, particularly case with protocols

From-SVN: r57250
parent 256e9fd2
Tue Sep 17 13:58:04 2002 Nicola Pero <n.pero@mi.flashnet.it>
Fix PR/7014 and related objc bugs:
* c-typeck.c (comp_target_types): Added a reflexive argument.
Pass it to ObjC when/if calling objc_comptypes(). Updated all
callers to provide the appropriate reflexive argument.
* objc/objc-act.c (objc_comptypes): Carefully checked and fixed
typechecking for all cases of comparisons and assignments,
particularly the obscure and less common ones involving protocols.
2002-09-17 Nick Clifton <nickc@redhat.com> 2002-09-17 Nick Clifton <nickc@redhat.com>
* machmode.def (V1DImode): New mode. A single element vector. * machmode.def (V1DImode): New mode. A single element vector.
......
...@@ -51,7 +51,7 @@ static int missing_braces_mentioned; ...@@ -51,7 +51,7 @@ static int missing_braces_mentioned;
static int undeclared_variable_notice; static int undeclared_variable_notice;
static tree qualify_type PARAMS ((tree, tree)); static tree qualify_type PARAMS ((tree, tree));
static int comp_target_types PARAMS ((tree, tree)); static int comp_target_types PARAMS ((tree, tree, int));
static int function_types_compatible_p PARAMS ((tree, tree)); static int function_types_compatible_p PARAMS ((tree, tree));
static int type_lists_compatible_p PARAMS ((tree, tree)); static int type_lists_compatible_p PARAMS ((tree, tree));
static tree decl_constant_value_for_broken_optimization PARAMS ((tree)); static tree decl_constant_value_for_broken_optimization PARAMS ((tree));
...@@ -579,16 +579,21 @@ comptypes (type1, type2) ...@@ -579,16 +579,21 @@ comptypes (type1, type2)
} }
/* Return 1 if TTL and TTR are pointers to types that are equivalent, /* Return 1 if TTL and TTR are pointers to types that are equivalent,
ignoring their qualifiers. */ ignoring their qualifiers. REFLEXIVE is only used by ObjC - set it
to 1 or 0 depending if the check of the pointer types is meant to
be reflexive or not (typically, assignments are not reflexive,
while comparisons are reflexive).
*/
static int static int
comp_target_types (ttl, ttr) comp_target_types (ttl, ttr, reflexive)
tree ttl, ttr; tree ttl, ttr;
int reflexive;
{ {
int val; int val;
/* Give objc_comptypes a crack at letting these types through. */ /* Give objc_comptypes a crack at letting these types through. */
if ((val = objc_comptypes (ttl, ttr, 1)) >= 0) if ((val = objc_comptypes (ttl, ttr, reflexive)) >= 0)
return val; return val;
val = comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)), val = comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)),
...@@ -1958,7 +1963,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p) ...@@ -1958,7 +1963,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p)
/* Subtraction of two similar pointers. /* Subtraction of two similar pointers.
We must subtract them as integers, then divide by object size. */ We must subtract them as integers, then divide by object size. */
if (code0 == POINTER_TYPE && code1 == POINTER_TYPE if (code0 == POINTER_TYPE && code1 == POINTER_TYPE
&& comp_target_types (type0, type1)) && comp_target_types (type0, type1, 1))
return pointer_diff (op0, op1); return pointer_diff (op0, op1);
/* Handle pointer minus int. Just like pointer plus int. */ /* Handle pointer minus int. Just like pointer plus int. */
else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE) else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
...@@ -2148,7 +2153,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p) ...@@ -2148,7 +2153,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p)
/* Anything compares with void *. void * compares with anything. /* Anything compares with void *. void * compares with anything.
Otherwise, the targets must be compatible Otherwise, the targets must be compatible
and both must be object or both incomplete. */ and both must be object or both incomplete. */
if (comp_target_types (type0, type1)) if (comp_target_types (type0, type1, 1))
result_type = common_type (type0, type1); result_type = common_type (type0, type1);
else if (VOID_TYPE_P (tt0)) else if (VOID_TYPE_P (tt0))
{ {
...@@ -2195,7 +2200,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p) ...@@ -2195,7 +2200,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p)
shorten = 1; shorten = 1;
else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
{ {
if (comp_target_types (type0, type1)) if (comp_target_types (type0, type1, 1))
{ {
result_type = common_type (type0, type1); result_type = common_type (type0, type1);
if (pedantic if (pedantic
...@@ -2220,7 +2225,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p) ...@@ -2220,7 +2225,7 @@ build_binary_op (code, orig_op0, orig_op1, convert_p)
short_compare = 1; short_compare = 1;
else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE) else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
{ {
if (comp_target_types (type0, type1)) if (comp_target_types (type0, type1, 1))
{ {
result_type = common_type (type0, type1); result_type = common_type (type0, type1);
if (!COMPLETE_TYPE_P (TREE_TYPE (type0)) if (!COMPLETE_TYPE_P (TREE_TYPE (type0))
...@@ -3443,7 +3448,7 @@ build_conditional_expr (ifexp, op1, op2) ...@@ -3443,7 +3448,7 @@ build_conditional_expr (ifexp, op1, op2)
} }
else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE) else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE)
{ {
if (comp_target_types (type1, type2)) if (comp_target_types (type1, type2, 1))
result_type = common_type (type1, type2); result_type = common_type (type1, type2);
else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node
&& TREE_CODE (orig_op1) != NOP_EXPR) && TREE_CODE (orig_op1) != NOP_EXPR)
...@@ -4010,8 +4015,9 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) ...@@ -4010,8 +4015,9 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype)) if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype))
{ {
overflow_warning (rhs); overflow_warning (rhs);
/* Check for Objective-C protocols. This will issue a warning if /* Check for Objective-C protocols. This will automatically
there are protocol violations. No need to use the return value. */ issue a warning if there are protocol violations. No need to
use the return value. */
if (flag_objc) if (flag_objc)
objc_comptypes (type, rhstype, 0); objc_comptypes (type, rhstype, 0);
return rhs; return rhs;
...@@ -4086,7 +4092,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) ...@@ -4086,7 +4092,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
Meanwhile, the lhs target must have all the qualifiers of Meanwhile, the lhs target must have all the qualifiers of
the rhs. */ the rhs. */
if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
|| comp_target_types (memb_type, rhstype)) || comp_target_types (memb_type, rhstype, 0))
{ {
/* If this type won't generate any warnings, use it. */ /* If this type won't generate any warnings, use it. */
if (TYPE_QUALS (ttl) == TYPE_QUALS (ttr) if (TYPE_QUALS (ttl) == TYPE_QUALS (ttr)
...@@ -4161,7 +4167,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) ...@@ -4161,7 +4167,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
and vice versa; otherwise, targets must be the same. and vice versa; otherwise, targets must be the same.
Meanwhile, the lhs target must have all the qualifiers of the rhs. */ Meanwhile, the lhs target must have all the qualifiers of the rhs. */
if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
|| comp_target_types (type, rhstype) || comp_target_types (type, rhstype, 0)
|| (c_common_unsigned_type (TYPE_MAIN_VARIANT (ttl)) || (c_common_unsigned_type (TYPE_MAIN_VARIANT (ttl))
== c_common_unsigned_type (TYPE_MAIN_VARIANT (ttr)))) == c_common_unsigned_type (TYPE_MAIN_VARIANT (ttr))))
{ {
...@@ -4186,7 +4192,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) ...@@ -4186,7 +4192,7 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
/* If this is not a case of ignoring a mismatch in signedness, /* If this is not a case of ignoring a mismatch in signedness,
no warning. */ no warning. */
else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
|| comp_target_types (type, rhstype)) || comp_target_types (type, rhstype, 0))
; ;
/* If there is a mismatch, do warn. */ /* If there is a mismatch, do warn. */
else if (pedantic) else if (pedantic)
......
...@@ -587,10 +587,24 @@ lookup_protocol_in_reflist (rproto_list, lproto) ...@@ -587,10 +587,24 @@ lookup_protocol_in_reflist (rproto_list, lproto)
return 0; return 0;
} }
/* Return 1 if LHS and RHS are compatible types for assignment /* Return 1 if LHS and RHS are compatible types for assignment or
or various other operations. Return 0 if they are incompatible, various other operations. Return 0 if they are incompatible, and
and return -1 if we choose to not decide. When the operation return -1 if we choose to not decide (because the types are really
is REFLEXIVE, check for compatibility in either direction. */ just C types, not ObjC specific ones). When the operation is
REFLEXIVE (typically comparisons), check for compatibility in
either direction; when it's not (typically assignments), don't.
This function is called in two cases: when both lhs and rhs are
pointers to records (in which case we check protocols too), and
when both lhs and rhs are records (in which case we check class
inheritance only).
Warnings about classes/protocols not implementing a protocol are
emitted here (multiple of those warnings might be emitted for a
single line!); generic warnings about incompatible assignments and
lacks of casts in comparisons are/must be emitted by the caller if
we return 0.
*/
int int
objc_comptypes (lhs, rhs, reflexive) objc_comptypes (lhs, rhs, reflexive)
...@@ -600,6 +614,8 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -600,6 +614,8 @@ objc_comptypes (lhs, rhs, reflexive)
{ {
/* New clause for protocols. */ /* New clause for protocols. */
/* Here we manage the case of a POINTER_TYPE = POINTER_TYPE. We only
manage the ObjC ones, and leave the rest to the C code. */
if (TREE_CODE (lhs) == POINTER_TYPE if (TREE_CODE (lhs) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE && TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE
&& TREE_CODE (rhs) == POINTER_TYPE && TREE_CODE (rhs) == POINTER_TYPE
...@@ -614,29 +630,75 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -614,29 +630,75 @@ objc_comptypes (lhs, rhs, reflexive)
tree rproto, rproto_list; tree rproto, rproto_list;
tree p; tree p;
/* <Protocol> = <Protocol> */
if (rhs_is_proto) if (rhs_is_proto)
{ {
rproto_list = TYPE_PROTOCOL_LIST (rhs); rproto_list = TYPE_PROTOCOL_LIST (rhs);
/* Make sure the protocol is supported by the object if (!reflexive)
on the rhs. */
for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto))
{ {
p = TREE_VALUE (lproto); /* An assignment between objects of type 'id
rproto = lookup_protocol_in_reflist (rproto_list, p); <Protocol>'; make sure the protocol on the lhs is
supported by the object on the rhs. */
for (lproto = lproto_list; lproto;
lproto = TREE_CHAIN (lproto))
{
p = TREE_VALUE (lproto);
rproto = lookup_protocol_in_reflist (rproto_list, p);
if (!rproto) if (!rproto)
warning ("object does not conform to the `%s' protocol", warning
IDENTIFIER_POINTER (PROTOCOL_NAME (p))); ("object does not conform to the `%s' protocol",
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
}
return 1;
}
else
{
/* Obscure case - a comparison between two objects
of type 'id <Protocol>'. Check that either the
protocol on the lhs is supported by the object on
the rhs, or viceversa. */
/* Check if the protocol on the lhs is supported by the
object on the rhs. */
for (lproto = lproto_list; lproto;
lproto = TREE_CHAIN (lproto))
{
p = TREE_VALUE (lproto);
rproto = lookup_protocol_in_reflist (rproto_list, p);
if (!rproto)
{
/* Check failed - check if the protocol on the rhs
is supported by the object on the lhs. */
for (rproto = rproto_list; rproto;
rproto = TREE_CHAIN (rproto))
{
p = TREE_VALUE (rproto);
lproto = lookup_protocol_in_reflist (lproto_list,
p);
if (!lproto)
{
/* This check failed too: incompatible */
return 0;
}
}
return 1;
}
}
return 1;
} }
} }
/* <Protocol> = <class> * */
else if (TYPED_OBJECT (TREE_TYPE (rhs))) else if (TYPED_OBJECT (TREE_TYPE (rhs)))
{ {
tree rname = TYPE_NAME (TREE_TYPE (rhs)); tree rname = TYPE_NAME (TREE_TYPE (rhs));
tree rinter; tree rinter;
/* Make sure the protocol is supported by the object /* Make sure the protocol is supported by the object on
on the rhs. */ the rhs. */
for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto))
{ {
p = TREE_VALUE (lproto); p = TREE_VALUE (lproto);
...@@ -651,7 +713,7 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -651,7 +713,7 @@ objc_comptypes (lhs, rhs, reflexive)
rproto = lookup_protocol_in_reflist (rproto_list, p); rproto = lookup_protocol_in_reflist (rproto_list, p);
/* If the underlying ObjC class does not have /* If the underlying ObjC class does not have
the protocol we're looking for, check for "one-off" the protocol we're looking for, check for "one-off"
protocols (e.g., `NSObject<MyProt> foo;') attached protocols (e.g., `NSObject<MyProt> *foo;') attached
to the rhs. */ to the rhs. */
if (!rproto) if (!rproto)
{ {
...@@ -665,7 +727,6 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -665,7 +727,6 @@ objc_comptypes (lhs, rhs, reflexive)
{ {
rproto_list = CLASS_PROTOCOL_LIST (cat); rproto_list = CLASS_PROTOCOL_LIST (cat);
rproto = lookup_protocol_in_reflist (rproto_list, p); rproto = lookup_protocol_in_reflist (rproto_list, p);
cat = CLASS_CATEGORY_LIST (cat); cat = CLASS_CATEGORY_LIST (cat);
} }
...@@ -674,31 +735,127 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -674,31 +735,127 @@ objc_comptypes (lhs, rhs, reflexive)
if (!rproto) if (!rproto)
warning ("class `%s' does not implement the `%s' protocol", warning ("class `%s' does not implement the `%s' protocol",
IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (rhs))), IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (rhs))),
IDENTIFIER_POINTER (PROTOCOL_NAME (p))); IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
} }
return 1;
} }
/* <Protocol> = id */
/* May change...based on whether there was any mismatch */ else if (TYPE_NAME (TREE_TYPE (rhs)) == objc_object_id)
return 1; {
return 1;
}
/* <Protocol> = Class */
else if (TYPE_NAME (TREE_TYPE (rhs)) == objc_class_id)
{
return 0;
}
/* <Protocol> = ?? : let comptypes decide. */
return -1;
} }
else if (rhs_is_proto) else if (rhs_is_proto)
/* Lhs is not a protocol...warn if it is statically typed */ {
return (TYPED_OBJECT (TREE_TYPE (lhs)) != 0); /* <class> * = <Protocol> */
if (TYPED_OBJECT (TREE_TYPE (lhs)))
{
if (reflexive)
{
tree rname = TYPE_NAME (TREE_TYPE (lhs));
tree rinter;
tree rproto, rproto_list = TYPE_PROTOCOL_LIST (rhs);
/* Make sure the protocol is supported by the object on
the lhs. */
for (rproto = rproto_list; rproto;
rproto = TREE_CHAIN (rproto))
{
tree p = TREE_VALUE (rproto);
tree lproto = 0;
rinter = lookup_interface (rname);
while (rinter && !lproto)
{
tree cat;
tree lproto_list = CLASS_PROTOCOL_LIST (rinter);
lproto = lookup_protocol_in_reflist (lproto_list, p);
/* If the underlying ObjC class does not
have the protocol we're looking for,
check for "one-off" protocols (e.g.,
`NSObject<MyProt> *foo;') attached to the
lhs. */
if (!lproto)
{
lproto_list = TYPE_PROTOCOL_LIST
(TREE_TYPE (lhs));
lproto = lookup_protocol_in_reflist
(lproto_list, p);
}
/* Check for protocols adopted by categories. */
cat = CLASS_CATEGORY_LIST (rinter);
while (cat && !lproto)
{
lproto_list = CLASS_PROTOCOL_LIST (cat);
lproto = lookup_protocol_in_reflist (lproto_list,
p);
cat = CLASS_CATEGORY_LIST (cat);
}
rinter = lookup_interface (CLASS_SUPER_NAME
(rinter));
}
if (!lproto)
warning ("class `%s' does not implement the `%s' protocol",
IDENTIFIER_POINTER (TYPE_NAME
(TREE_TYPE (lhs))),
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
}
return 1;
}
else
return 0;
}
/* id = <Protocol> */
else if (TYPE_NAME (TREE_TYPE (lhs)) == objc_object_id)
{
return 1;
}
/* Class = <Protocol> */
else if (TYPE_NAME (TREE_TYPE (lhs)) == objc_class_id)
{
return 0;
}
/* ??? = <Protocol> : let comptypes decide */
else
{
return -1;
}
}
else else
/* Defer to comptypes. */ {
return -1; /* Attention: we shouldn't defer to comptypes here. One bad
side effect would be that we might loose the REFLEXIVE
information.
*/
lhs = TREE_TYPE (lhs);
rhs = TREE_TYPE (rhs);
}
} }
else if (TREE_CODE (lhs) == RECORD_TYPE && TREE_CODE (rhs) == RECORD_TYPE) if (TREE_CODE (lhs) != RECORD_TYPE || TREE_CODE (rhs) != RECORD_TYPE)
; /* Fall thru. This is the case we have been handling all along */ {
else /* Nothing to do with ObjC - let immediately comptypes take
/* Defer to comptypes. */ responsibility for checking. */
return -1; return -1;
}
/* `id' = `<class> *', `<class> *' = `id' */
/* `id' = `<class> *' `<class> *' = `id': always allow it.
Please note that
'Object *o = [[Object alloc] init]; falls
in the case <class> * = `id'.
*/
if ((TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs)) if ((TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs))
|| (TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs))) || (TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs)))
return 1; return 1;
...@@ -739,7 +896,7 @@ objc_comptypes (lhs, rhs, reflexive) ...@@ -739,7 +896,7 @@ objc_comptypes (lhs, rhs, reflexive)
return 0; return 0;
} }
else else
/* Defer to comptypes. */ /* Not an ObjC type - let comptypes do the check. */
return -1; return -1;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment