static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
static tree handle_nsobject_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *);
+static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *);
/* Helper to define attribute exclusions. */
#define ATTR_EXCL(name, function, type, variable) \
handle_nsobject_attribute, NULL },
{ "objc_root_class", 0, 0, true, false, false, false,
handle_objc_root_class_attribute, NULL },
+ { "objc_nullability", 1, 1, true, false, false, false,
+ handle_objc_nullability_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
return NULL_TREE;
}
+/* Handle an "objc_nullability" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_objc_nullability_attribute (tree *node, tree name, tree args,
+ int /*flags*/,
+ bool *no_add_attrs)
+{
+ *no_add_attrs = true;
+
+ tree type = TREE_TYPE (*node);
+ if (TREE_CODE (*node) == FUNCTION_DECL)
+ type = TREE_TYPE (type);
+
+ if (type && !POINTER_TYPE_P (type))
+ {
+ error ("%qE cannot be applied to non-pointer type %qT", name, type);
+ return NULL_TREE;
+ }
+
+ /* We accept objc_nullability() with a single argument.
+ string: "unspecified", "nullable", "nonnull" or "resettable"
+ integer: 0 and 3 where the values have the same meaning as
+ the strings. */
+ tree val = TREE_VALUE (args);
+ if (TREE_CODE (val) == INTEGER_CST)
+ {
+ val = default_conversion (val);
+ if (!tree_fits_uhwi_p (val) || tree_to_uhwi (val) > 3)
+ error ("%qE attribute argument %qE is not an integer constant"
+ " between 0 and 3", name, val);
+ else
+ *no_add_attrs = false; /* OK */
+ }
+ else if (TREE_CODE (val) == STRING_CST
+ && (strcmp (TREE_STRING_POINTER (val), "nullable") == 0
+ || strcmp (TREE_STRING_POINTER (val), "nonnull") == 0
+ || strcmp (TREE_STRING_POINTER (val), "unspecified") == 0
+ || strcmp (TREE_STRING_POINTER (val), "resettable") == 0))
+ *no_add_attrs = false; /* OK */
+ else if (val != error_mark_node)
+ error ("%qE attribute argument %qE is not recognised", name, val);
+
+ return NULL_TREE;
+}
+
/* Attempt to partially validate a single attribute ATTR as if
it were to be applied to an entity OPER. */
{ "readwrite", RID_READWRITE, D_OBJC },
{ "retain", RID_RETAIN, D_OBJC },
{ "setter", RID_SETTER, D_OBJC },
+ /* These are Objective C implementation of nullability, accepted only in
+ specific contexts. */
+ { "null_unspecified", RID_NULL_UNSPECIFIED, D_OBJC },
+ { "nullable", RID_NULLABLE, D_OBJC },
+ { "nonnull", RID_NONNULL, D_OBJC },
+ { "null_resettable", RID_NULL_RESETTABLE, D_OBJC },
};
const unsigned int num_c_common_reswords =
RID_ASSIGN, RID_RETAIN, RID_COPY,
RID_PROPATOMIC, RID_NONATOMIC,
+ /* ObjC nullability support keywords that also can appear in the
+ property attribute context. These values should remain contiguous
+ with the other property attributes. */
+ RID_NULL_UNSPECIFIED, RID_NULLABLE, RID_NONNULL, RID_NULL_RESETTABLE,
+
/* C (reserved and imaginary types not implemented, so any use is a
syntax error) */
RID_IMAGINARY,
RID_FIRST_PQ = RID_IN,
RID_LAST_PQ = RID_ONEWAY,
RID_FIRST_PATTR = RID_GETTER,
- RID_LAST_PATTR = RID_NONATOMIC
+ RID_LAST_PATTR = RID_NULL_RESETTABLE
};
#define OBJC_IS_AT_KEYWORD(rid) \
OBJC_PROPATTR_GROUP_READWRITE,
OBJC_PROPATTR_GROUP_ASSIGN,
OBJC_PROPATTR_GROUP_ATOMIC,
+ OBJC_PROPATTR_GROUP_NULLABLE,
OBJC_PROPATTR_GROUP_CLASS,
OBJC_PROPATTR_GROUP_MAX
};
OBJC_PROPERTY_ATTR_COPY = ( 7 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
OBJC_PROPERTY_ATTR_ATOMIC = ( 8 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
OBJC_PROPERTY_ATTR_NONATOMIC = ( 9 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
+ OBJC_PROPERTY_ATTR_NULL_UNSPECIFIED = (12 << 8)|OBJC_PROPATTR_GROUP_NULLABLE,
+ OBJC_PROPERTY_ATTR_NULLABLE = (13 << 8)|OBJC_PROPATTR_GROUP_NULLABLE,
+ OBJC_PROPERTY_ATTR_NONNULL = (14 << 8)|OBJC_PROPATTR_GROUP_NULLABLE,
+ OBJC_PROPERTY_ATTR_NULL_RESETTABLE = (15 << 8)|OBJC_PROPATTR_GROUP_NULLABLE,
OBJC_PROPERTY_ATTR_CLASS = (16 << 8)|OBJC_PROPATTR_GROUP_CLASS,
OBJC_PROPERTY_ATTR_MAX = (255 << 8|OBJC_PROPATTR_GROUP_MAX)
};
specific to ELF targets and relies on the linker to place such data in
the right location
+@item objc_nullability (@var{nullability kind}) @r{(Objective-C and Objective
+-C++ only)}
+@cindex @code{objc_nullability} variable attribute
+This attribute applies to pointer variables only. It allows marking the
+pointer with one of four possible values describing the conditions under
+which the pointer might have a @code{nil} value. In most cases, the
+attribute is intended to be an internal representation for property and
+method nullability (specified by language keywords); it is not recommended
+to use it directly.
+
+When @var{nullability kind} is @code{"unspecified"} or @code{0}, nothing is
+known about the conditions in which the pointer might be @code{nil}. Making
+this state specific serves to avoid false positives in diagnostics.
+
+When @var{nullability kind} is @code{"nonnull"} or @code{1}, the pointer has
+no meaning if it is @code{nil} and thus the compiler is free to emit
+diagnostics if it can be determined that the value will be @code{nil}.
+
+When @var{nullability kind} is @code{"nullable"} or @code{2}, the pointer might
+be @code{nil} and carry meaning as such.
+
+When @var{nullability kind} is @code{"resettable"} or @code{3} (used only in
+the context of property attribute lists) this describes the case in which a
+property setter may take the value @code{nil} (which perhaps causes the
+property to be reset in some manner to a default) but for which the property
+getter will never validly return @code{nil}.
+
@end table
@node ARC Variable Attributes
case RID_PROPATOMIC: return OBJC_PROPERTY_ATTR_ATOMIC;
case RID_NONATOMIC: return OBJC_PROPERTY_ATTR_NONATOMIC;
+ case RID_NULL_UNSPECIFIED:return OBJC_PROPERTY_ATTR_NULL_UNSPECIFIED;
+ case RID_NULLABLE: return OBJC_PROPERTY_ATTR_NULLABLE;
+ case RID_NONNULL: return OBJC_PROPERTY_ATTR_NONNULL;
+ case RID_NULL_RESETTABLE: return OBJC_PROPERTY_ATTR_NULL_RESETTABLE;
+
case RID_CLASS: return OBJC_PROPERTY_ATTR_CLASS;
}
}
property_nonatomic = attrs[OBJC_PROPATTR_GROUP_CLASS]->prop_kind
== OBJC_PROPERTY_ATTR_CLASS;
+ /* Nullability specifications for the property. */
+ enum objc_property_nullability property_nullability
+ = OBJC_PROPERTY_NULL_UNSET;
+ if (attrs[OBJC_PROPATTR_GROUP_NULLABLE])
+ {
+ if (attrs[OBJC_PROPATTR_GROUP_NULLABLE]->prop_kind
+ == OBJC_PROPERTY_ATTR_NULL_UNSPECIFIED)
+ property_nullability = OBJC_PROPERTY_NULL_UNSPECIFIED;
+ else if (attrs[OBJC_PROPATTR_GROUP_NULLABLE]->prop_kind
+ == OBJC_PROPERTY_ATTR_NULLABLE)
+ property_nullability = OBJC_PROPERTY_NULLABLE;
+ else if (attrs[OBJC_PROPATTR_GROUP_NULLABLE]->prop_kind
+ == OBJC_PROPERTY_ATTR_NONNULL)
+ property_nullability = OBJC_PROPERTY_NONNULL;
+ else if (attrs[OBJC_PROPATTR_GROUP_NULLABLE]->prop_kind
+ == OBJC_PROPERTY_ATTR_NULL_RESETTABLE)
+ property_nullability = OBJC_PROPERTY_NULL_RESETTABLE;
+ else
+ gcc_unreachable ();
+ }
+
/* TODO: Check that the property type is an Objective-C object or a
"POD". */
tree property_decl = make_node (PROPERTY_DECL);
/* Copy the basic information from the original decl. */
- TREE_TYPE (property_decl) = TREE_TYPE (decl);
+ tree p_type = TREE_TYPE (decl);
+ TREE_TYPE (property_decl) = p_type;
DECL_SOURCE_LOCATION (property_decl) = DECL_SOURCE_LOCATION (decl);
TREE_DEPRECATED (property_decl) = TREE_DEPRECATED (decl);
PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
PROPERTY_DYNAMIC (property_decl) = 0;
+ /* FIXME: We seem to drop any existing DECL_ATTRIBUTES on the floor. */
+ if (property_nullability != OBJC_PROPERTY_NULL_UNSET)
+ {
+ if (p_type && !POINTER_TYPE_P (p_type))
+ error_at (decl_loc, "nullability specifier %qE cannot be applied to"
+ " non-pointer type %qT",
+ attrs[OBJC_PROPATTR_GROUP_NULLABLE]->name, p_type);
+ else if (p_type && POINTER_TYPE_P (p_type) && TREE_TYPE (p_type)
+ && POINTER_TYPE_P (TREE_TYPE (p_type)))
+ error_at (decl_loc, "nullability specifier %qE cannot be applied to"
+ " multi-level pointer type %qT",
+ attrs[OBJC_PROPATTR_GROUP_NULLABLE]->name, p_type);
+ else
+ {
+ tree attr_name = get_identifier ("objc_nullability");
+ tree attr_value = build_int_cst (unsigned_type_node,
+ (unsigned)property_nullability);
+ tree nulla = build_tree_list (attr_name, attr_value);
+ DECL_ATTRIBUTES (property_decl) = nulla;
+ }
+ }
+
/* Remember the fact that the property was found in the @optional
section in a @protocol, or not. */
if (objc_method_optional_flag)
#define PROPERTY_CLASS(DECL) \
DECL_LANG_FLAG_6 (PROPERTY_DECL_CHECK (DECL))
+/* PROPERTY_NULLABILITY attributes added to the decl attributes.
+ effectively, __attribute__((objc_nullability(kind))), */
+enum objc_property_nullability {
+ OBJC_PROPERTY_NULL_UNSPECIFIED = 0,
+ OBJC_PROPERTY_NULLABLE,
+ OBJC_PROPERTY_NONNULL,
+ OBJC_PROPERTY_NULL_RESETTABLE,
+ OBJC_PROPERTY_NULL_UNSET
+};
+
/* PROPERTY_REF. A PROPERTY_REF represents an 'object.property'
expression. It is normally used for property access, but when
the Objective-C 2.0 "dot-syntax" (object.component) is used
--- /dev/null
+/* { dg-do compile } */
+/* { dg-additional-options "-Wno-objc-root-class -fsyntax-only" } */
+
+__attribute__((objc_nullability(0))) id a;
+__attribute__((objc_nullability(4))) id e_1; /* { dg-error {'objc_nullability' attribute argument '4' is not an integer constant between 0 and 3} } */
+__attribute__((objc_nullability(-22))) id e_2; /* { dg-error {'objc_nullability' attribute argument '-22' is not an integer constant between 0 and 3} } */
+__attribute__((objc_nullability("unspecified"))) id b;
+__attribute__((objc_nullability("nullable"))) id c;
+__attribute__((objc_nullability("nonnull"))) id d;
+__attribute__((objc_nullability("resettable"))) id e;
+__attribute__((objc_nullability("nonsense"))) id e_3; /* { dg-error {'objc_nullability' attribute argument '"nonsense"' is not recognised} } */
+__attribute__((objc_nullability(noGoingToWork))) id e_4; /* { dg-error {'noGoingToWork' was not declared in this scope} } */
+
+@interface MyRoot
+{
+ __attribute__((objc_nullability(0))) id iv_a;
+ __attribute__((objc_nullability(3))) struct { int bad_a; } s;/* { dg-error {'objc_nullability' cannot be applied to non-pointer type '<unnamed struct>'} } */
+ __attribute__((objc_nullability("resettable"))) int iv_b;/* { dg-error {'objc_nullability' cannot be applied to non-pointer type 'int'} } */
+}
+@end
@property (class) int property_cl_1;
+@property (null_unspecified) int *property_null_1;
+@property (nullable) int *property_null_2;
+@property (nonnull) int *property_null_3;
+@property (null_resettable) int *property_null_4;
+
@property (release) int property_err_1; /* { dg-error "unknown property attribute" } */
@property (getter=myGetter) int property_g0;
@property (setter=mySetter:) int property_s0;
-/* Now test various problems. */
+/* Now test various basic problems. */
@property (readonly, readwrite) int a; /* { dg-error ".readwrite. attribute conflicts with .readonly. attribute" } */
@property (readonly, setter=mySetterB:) int b; /* { dg-error ".readonly. attribute conflicts with .setter. attribute" } */
@property (atomic, nonatomic) int property_j; /* { dg-error {'nonatomic' attribute conflicts with 'atomic' attribute} } */
+@property (null_unspecified) int property_bad_t_1; /* { dg-error {nullability specifier 'null_unspecified' cannot be applied to non-pointer type 'int'} } */
+@property (nullable) int property_bad_t_2;/* { dg-error {nullability specifier 'nullable' cannot be applied to non-pointer type 'int'} } */
+@property (nonnull) int property_bad_t_3;/* { dg-error {nullability specifier 'nonnull' cannot be applied to non-pointer type 'int'} } */
+@property (null_resettable) int property_bad_t_4;/* { dg-error {nullability specifier 'null_resettable' cannot be applied to non-pointer type 'int'} } */
+@property (nullable) int **property_bad_t_5;/* { dg-error {nullability specifier 'nullable' cannot be applied to multi-level pointer type 'int\*\*'} } */
+
+@property (null_unspecified, nullable) int *property_ne_1; /* { dg-error {'nullable' attribute conflicts with 'null_unspecified' attribute} } */
+@property (null_unspecified, nonnull) int *property_ne_2; /* { dg-error {'nonnull' attribute conflicts with 'null_unspecified' attribute} } */
+@property (null_unspecified, null_resettable) int *property_ne_3; /* { dg-error {'null_resettable' attribute conflicts with 'null_unspecified' attribute} } */
+@property (nullable,nonnull) int *property_ne_4; /* { dg-error {'nonnull' attribute conflicts with 'nullable' attribute} } */
+@property (nullable,null_resettable) int *property_ne_5; /* { dg-error {'null_resettable' attribute conflicts with 'nullable' attribute} } */
+@property (nonnull, null_resettable) int *property_ne_6; /* { dg-error {'null_resettable' attribute conflicts with 'nonnull' attribute} } */
+
@property (setter=mySetter:,setter=mySetter2:) int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
@property (getter=myGetter, getter=myGetter2 ) int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-additional-options "-fsyntax-only" } */
+
+@interface MyRoot
+{
+ Class isa __attribute__((deprecated));
+ id p;
+ int x;
+ int *i;
+}
+
+@property(null_unspecified, assign) MyRoot *p1;
+@property(nonnull, assign) MyRoot *p2;
+@property(nullable, assign) MyRoot *p3;
+@property(null_resettable, assign) MyRoot *p4;
+@property(null_exciting, assign) MyRoot *e_5; /* { dg-error {unknown property attribute 'null_exciting'} } */
+
+@property(nonnull, retain, nullable) MyRoot *e_6; /* { dg-error {'nullable' attribute conflicts with 'nonnull' attribute} } */
+@property(nonnull, nonnull) int *i; /* { dg-warning {duplicate 'nonnull' attribute} } */
+
+@end
--- /dev/null
+/* { dg-do compile } */
+/* { dg-additional-options "-Wno-objc-root-class -fsyntax-only" } */
+
+__attribute__((objc_nullability(0))) id a;
+__attribute__((objc_nullability(4))) id e_1; /* { dg-error {'objc_nullability' attribute argument '4' is not an integer constant between 0 and 3} } */
+__attribute__((objc_nullability(-22))) id e_2; /* { dg-error {'objc_nullability' attribute argument '-22' is not an integer constant between 0 and 3} } */
+__attribute__((objc_nullability("unspecified"))) id b;
+__attribute__((objc_nullability("nullable"))) id c;
+__attribute__((objc_nullability("nonnull"))) id d;
+__attribute__((objc_nullability("resettable"))) id e;
+__attribute__((objc_nullability("nonsense"))) id e_3; /* { dg-error {'objc_nullability' attribute argument '"nonsense"' is not recognised} } */
+__attribute__((objc_nullability(noGoingToWork))) id e_4; /* { dg-error {'noGoingToWork' undeclared here} } */
+
+@interface MyRoot
+{
+ __attribute__((objc_nullability(0))) id iv_a;
+ __attribute__((objc_nullability(3))) struct { int bad_a; } s;/* { dg-error {'objc_nullability' cannot be applied to non-pointer type 'struct <anonymous>'} } */
+ __attribute__((objc_nullability("resettable"))) int iv_b;/* { dg-error {'objc_nullability' cannot be applied to non-pointer type 'int'} } */
+}
+@end
@property (class) int property_cl_1;
+@property (null_unspecified) int *property_null_1;
+@property (nullable) int *property_null_2;
+@property (nonnull) int *property_null_3;
+@property (null_resettable) int *property_null_4;
+
@property (release) int property_err_1; /* { dg-error "unknown property attribute" } */
@property (getter=myGetter) int property_h;
@property (atomic, nonatomic) int property_j; /* { dg-error {'nonatomic' attribute conflicts with 'atomic' attribute} } */
+@property (null_unspecified) int property_bad_t_1; /* { dg-error {nullability specifier 'null_unspecified' cannot be applied to non-pointer type 'int'} } */
+@property (nullable) int property_bad_t_2;/* { dg-error {nullability specifier 'nullable' cannot be applied to non-pointer type 'int'} } */
+@property (nonnull) int property_bad_t_3;/* { dg-error {nullability specifier 'nonnull' cannot be applied to non-pointer type 'int'} } */
+@property (null_resettable) int property_bad_t_4;/* { dg-error {nullability specifier 'null_resettable' cannot be applied to non-pointer type 'int'} } */
+@property (nullable) int **property_bad_t_5;/* { dg-error {nullability specifier 'nullable' cannot be applied to multi-level pointer type 'int \*\*'} } */
+
+@property (null_unspecified, nullable) int *property_ne_1; /* { dg-error {'nullable' attribute conflicts with 'null_unspecified' attribute} } */
+@property (null_unspecified, nonnull) int *property_ne_2; /* { dg-error {'nonnull' attribute conflicts with 'null_unspecified' attribute} } */
+@property (null_unspecified, null_resettable) int *property_ne_3; /* { dg-error {'null_resettable' attribute conflicts with 'null_unspecified' attribute} } */
+@property (nullable,nonnull) int *property_ne_4; /* { dg-error {'nonnull' attribute conflicts with 'nullable' attribute} } */
+@property (nullable,null_resettable) int *property_ne_5; /* { dg-error {'null_resettable' attribute conflicts with 'nullable' attribute} } */
+@property (nonnull, null_resettable) int *property_ne_6; /* { dg-error {'null_resettable' attribute conflicts with 'nonnull' attribute} } */
+
@property (setter=mySetter:,setter=mySetter2:) int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
@property (getter=myGetter, getter=myGetter2 ) int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-additional-options "-fsyntax-only" } */
+
+@interface MyRoot
+{
+ Class isa __attribute__((deprecated));
+ id p;
+ int x;
+ int *i;
+}
+
+@property(null_unspecified, assign) MyRoot *p1;
+@property(nonnull, assign) MyRoot *p2;
+@property(nullable, assign) MyRoot *p3;
+@property(null_resettable, assign) MyRoot *p4;
+@property(null_exciting, assign) MyRoot *e_5; /* { dg-error {unknown property attribute 'null_exciting'} } */
+
+@property(nonnull, retain, nullable) MyRoot *e_6; /* { dg-error {'nullable' attribute conflicts with 'nonnull' attribute} } */
+@property(nonnull, nonnull) int *i; /* { dg-warning {duplicate 'nonnull' attribute} } */
+
+@end