diff options
author | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:57:53 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:57:53 +0000 |
commit | e83b95641f911e44c8c092bc0eef7743df0cd73d (patch) | |
tree | 88c82874e35786d877ab648a3d792dc62bcfda68 /clang/test | |
parent | e9d95f1ecc98ced831cace8b4b78cb7cc380f4aa (diff) | |
download | bcm5719-llvm-e83b95641f911e44c8c092bc0eef7743df0cd73d.tar.gz bcm5719-llvm-e83b95641f911e44c8c092bc0eef7743df0cd73d.zip |
Substitute type arguments into uses of Objective-C interface members.
When messaging a method that was defined in an Objective-C class (or
category or extension thereof) that has type parameters, substitute
the type arguments for those type parameters. Similarly, substitute
into property accesses, instance variables, and other references.
This includes general infrastructure for substituting the type
arguments associated with an ObjCObject(Pointer)Type into a type
referenced within a particular context, handling all of the
substitutions required to deal with (e.g.) inheritance involving
parameterized classes. In cases where no type arguments are available
(e.g., because we're messaging via some unspecialized type, id, etc.),
we substitute in the type bounds for the type parameters instead.
Example:
@interface NSSet<T : id<NSCopying>> : NSObject <NSCopying>
- (T)firstObject;
@end
void f(NSSet<NSString *> *stringSet, NSSet *anySet) {
[stringSet firstObject]; // produces NSString*
[anySet firstObject]; // produces id<NSCopying> (the bound)
}
When substituting for the type parameters given an unspecialized
context (i.e., no specific type arguments were given), substituting
the type bounds unconditionally produces type signatures that are too
strong compared to the pre-generics signatures. Instead, use the
following rule:
- In covariant positions, such as method return types, replace type
parameters with “id” or “Class” (the latter only when the type
parameter bound is “Class” or qualified class, e.g,
“Class<NSCopying>”)
- In other positions (e.g., parameter types), replace type
parameters with their type bounds.
- When a specialized Objective-C object or object pointer type
contains a type parameter in its type arguments (e.g.,
NSArray<T>*, but not NSArray<NSString *> *), replace the entire
object/object pointer type with its unspecialized version (e.g.,
NSArray *).
llvm-svn: 241543
Diffstat (limited to 'clang/test')
-rw-r--r-- | clang/test/CodeGenObjC/parameterized_classes.m | 63 | ||||
-rw-r--r-- | clang/test/PCH/objc_parameterized_classes.m | 5 | ||||
-rw-r--r-- | clang/test/SemaObjC/parameterized_classes.m | 18 | ||||
-rw-r--r-- | clang/test/SemaObjC/parameterized_classes_subst.m | 372 | ||||
-rw-r--r-- | clang/test/SemaObjCXX/Inputs/nullability-pragmas-generics-1.h | 21 | ||||
-rw-r--r-- | clang/test/SemaObjCXX/nullability-pragmas.mm | 12 |
6 files changed, 475 insertions, 16 deletions
diff --git a/clang/test/CodeGenObjC/parameterized_classes.m b/clang/test/CodeGenObjC/parameterized_classes.m new file mode 100644 index 00000000000..1d8e9a25993 --- /dev/null +++ b/clang/test/CodeGenObjC/parameterized_classes.m @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fblocks -fobjc-arc -emit-llvm -o - %s | FileCheck %s + +// Parameterized classes have no effect on code generation; this test +// mainly verifies that CodeGen doesn't assert when substituted types +// in uses of methods don't line up exactly with the parameterized +// types in the method declarations due to type erasure. "Not crash" +// is the only interesting criteria here. + +@protocol NSObject +@end + +@protocol NSCopying +@end + +__attribute__((objc_root_class)) +@interface NSObject <NSObject> +@end + +@interface NSString : NSObject <NSCopying> +@end + +@interface NSMutableArray<T> : NSObject <NSCopying> +@property (copy,nonatomic) T firstObject; +- (void)addObject:(T)object; +- (void)sortWithFunction:(int (*)(T, T))function; +- (void)getObjects:(T __strong *)objects length:(unsigned*)length; +@end + +NSString *getFirstObjectProp(NSMutableArray<NSString *> *array) { + return array.firstObject; +} + +NSString *getFirstObjectMethod(NSMutableArray<NSString *> *array) { + return [array firstObject]; +} + +void addObject(NSMutableArray<NSString *> *array, NSString *obj) { + [array addObject: obj]; +} + +int compareStrings(NSString *x, NSString *y) { return 0; } +int compareBlocks(NSString * (^x)(NSString *), + NSString * (^y)(NSString *)) { return 0; } + +void sortTest(NSMutableArray<NSString *> *array, + NSMutableArray<NSString * (^)(NSString *)> *array2) { + [array sortWithFunction: &compareStrings]; + [array2 sortWithFunction: &compareBlocks]; +} + +void getObjectsTest(NSMutableArray<NSString *> *array) { + NSString * __strong *objects; + unsigned length; + [array getObjects: objects length: &length]; +} + +void printMe(NSString *name) { } + +// CHECK-LABEL: define void @blockTest +void blockTest(NSMutableArray<void (^)(void)> *array, NSString *name) { + // CHECK: call i8* @objc_retainBlock + [array addObject: ^ { printMe(name); }]; +} diff --git a/clang/test/PCH/objc_parameterized_classes.m b/clang/test/PCH/objc_parameterized_classes.m index f4fd0926be3..616d4a2659d 100644 --- a/clang/test/PCH/objc_parameterized_classes.m +++ b/clang/test/PCH/objc_parameterized_classes.m @@ -21,6 +21,7 @@ __attribute__((objc_root_class)) typedef PC1<id, NSObject *> PC1Specialization1; +typedef PC1Specialization1 <NSObject> PC1Specialization2; #else @interface PC1<T : NSObject *, // expected-error{{type bound 'NSObject *' for type parameter 'T' conflicts with implicit bound 'id}} @@ -29,6 +30,8 @@ typedef PC1<id, NSObject *> PC1Specialization1; // expected-note@15{{type parameter 'U' declared here}} @end -typedef PC1Specialization1<id, NSObject *> PC1Specialization2; // expected-error{{type arguments cannot be applied to already-specialized class type 'PC1Specialization1' (aka 'PC1<id,NSObject *>')}} +typedef PC1Specialization1<id, NSObject *> PC1Specialization3; // expected-error{{type arguments cannot be applied to already-specialized class type 'PC1Specialization1' (aka 'PC1<id,NSObject *>')}} + +typedef PC1Specialization2<id, NSObject *> PC1Specialization4; // expected-error{{already-specialized class type 'PC1Specialization2' (aka 'PC1Specialization1<NSObject>')}} #endif diff --git a/clang/test/SemaObjC/parameterized_classes.m b/clang/test/SemaObjC/parameterized_classes.m index bf81ad9a078..9834f7cd11f 100644 --- a/clang/test/SemaObjC/parameterized_classes.m +++ b/clang/test/SemaObjC/parameterized_classes.m @@ -173,29 +173,17 @@ __attribute__((objc_root_class)) T object; } -- (U)method:(V)param; // expected-note{{passing argument to parameter 'param' here}} +- (U)method:(V)param; @end @interface PC20<T, U, V> (Cat1) -- (U)catMethod:(V)param; // expected-note{{passing argument to parameter 'param' here}} +- (U)catMethod:(V)param; @end @interface PC20<X, Y, Z>() -- (X)extMethod:(Y)param; // expected-note{{passing argument to parameter 'param' here}} +- (X)extMethod:(Y)param; @end -void test_PC20_unspecialized(PC20 *pc20) { - // FIXME: replace type parameters with underlying types? - int *ip = [pc20 method: 0]; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'U' (aka 'NSObject *')}} - [pc20 method: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'V' (aka 'NSString *')}} - - ip = [pc20 catMethod: 0]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'U' (aka 'NSObject *')}} - [pc20 catMethod: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'V' (aka 'NSString *')}} - - ip = [pc20 extMethod: 0]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'X' (aka 'id')}} - [pc20 extMethod: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'Y' (aka 'NSObject *')}} -} - // -------------------------------------------------------------------------- // Parsing type arguments. // -------------------------------------------------------------------------- diff --git a/clang/test/SemaObjC/parameterized_classes_subst.m b/clang/test/SemaObjC/parameterized_classes_subst.m new file mode 100644 index 00000000000..186800cddd7 --- /dev/null +++ b/clang/test/SemaObjC/parameterized_classes_subst.m @@ -0,0 +1,372 @@ +// RUN: %clang_cc1 -fblocks -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify +// +// Test the substitution of type arguments for type parameters when +// using parameterized classes in Objective-C. + +__attribute__((objc_root_class)) +@interface NSObject ++ (instancetype)alloc; +- (instancetype)init; +@end + +@protocol NSCopying +@end + +@interface NSString : NSObject <NSCopying> +@end + +@interface NSNumber : NSObject <NSCopying> +@end + +@interface NSArray<T> : NSObject <NSCopying> { +@public + T *data; // don't try this at home +} +- (T)objectAtIndexedSubscript:(int)index; ++ (NSArray<T> *)array; ++ (void)setArray:(NSArray <T> *)array; +@property (copy,nonatomic) T lastObject; +@end + +@interface NSMutableArray<T> : NSArray<T> +-(instancetype)initWithArray:(NSArray<T> *)array; // expected-note{{passing argument}} +- (void)setObject:(T)object atIndexedSubscript:(int)index; // expected-note 2{{passing argument to parameter 'object' here}} +@end + +@interface NSStringArray : NSArray<NSString *> +@end + +@interface NSSet<T> : NSObject <NSCopying> +- (T)firstObject; +@property (nonatomic, copy) NSArray<T> *allObjects; +@end + +// Parameterized inheritance (simple case) +@interface NSMutableSet<U : id<NSCopying>> : NSSet<U> +- (void)addObject:(U)object; // expected-note 7{{passing argument to parameter 'object' here}} +@end + +@interface Widget : NSObject <NSCopying> +@end + +// Non-parameterized class inheriting from a specialization of a +// parameterized class. +@interface WidgetSet : NSMutableSet<Widget *> +@end + +// Parameterized inheritance with a more interesting transformation in +// the specialization. +@interface MutableSetOfArrays<T> : NSMutableSet<NSArray<T>*> +@end + +// Inheriting from an unspecialized form of a parameterized type. +@interface UntypedMutableSet : NSMutableSet +@end + +@interface Window : NSObject +@end + +@interface NSDictionary<K, V> : NSObject <NSCopying> +- (V)objectForKeyedSubscript:(K)key; // expected-note 2{{parameter 'key'}} +@end + +@interface NSMutableDictionary<K : id<NSCopying>, V> : NSDictionary<K, V> +- (void)setObject:(V)object forKeyedSubscript:(K)key; +// expected-note@-1 {{parameter 'object' here}} +// expected-note@-2 {{parameter 'object' here}} +// expected-note@-3 {{parameter 'key' here}} +// expected-note@-4 {{parameter 'key' here}} + +@property (strong) K someRandomKey; +@end + +@interface WindowArray : NSArray<Window *> +@end + +@interface NSSet<T> (Searching) +- (T)findObject:(T)object; +@end + +@interface NSView : NSObject +@end + +@interface NSControl : NSView +- (void)toggle; +@end + +@interface NSViewController<ViewType : NSView *> : NSObject +@property (nonatomic,retain) ViewType view; +@end + +// -------------------------------------------------------------------------- +// Nullability +// -------------------------------------------------------------------------- +typedef NSControl * _Nonnull Nonnull_NSControl; + +@interface NSNullableTest<ViewType : NSView *> : NSObject +- (ViewType)view; +- (nullable ViewType)maybeView; +@end + +@interface NSNullableTest2<ViewType : NSView * _Nullable> : NSObject // expected-error{{type parameter 'ViewType' bound 'NSView * _Nullable' cannot explicitly specify nullability}} +@end + +void test_nullability(void) { + NSControl * _Nonnull nonnull_NSControl; + + // Nullability introduced by substitution. + NSNullableTest<NSControl *> *unspecifiedControl; + nonnull_NSControl = [unspecifiedControl view]; + nonnull_NSControl = [unspecifiedControl maybeView]; // expected-warning{{from nullable pointer 'NSControl * _Nullable' to non-nullable pointer type 'NSControl * _Nonnull'}} + + // Nullability overridden by substitution. + NSNullableTest<Nonnull_NSControl> *nonnullControl; + nonnull_NSControl = [nonnullControl view]; + nonnull_NSControl = [nonnullControl maybeView]; // expected-warning{{from nullable pointer 'Nonnull_NSControl _Nullable' (aka 'NSControl *') to non-nullable pointer type 'NSControl * _Nonnull'}} + + // Nullability cannot be specified directly on a type argument. + NSNullableTest<NSControl * _Nonnull> *nonnullControl2; // expected-error{{type argument 'NSControl *' cannot explicitly specify nullability}} +} + +// -------------------------------------------------------------------------- +// Message sends. +// -------------------------------------------------------------------------- +void test_message_send_result( + NSSet<NSString *> *stringSet, + NSMutableSet<NSString *> *mutStringSet, + WidgetSet *widgetSet, + UntypedMutableSet *untypedMutSet, + MutableSetOfArrays<NSString *> *mutStringArraySet, + NSSet *set, + NSMutableSet *mutSet, + MutableSetOfArrays *mutArraySet, + NSArray<NSString *> *stringArray, + void (^block)(void)) { + int *ip; + ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}} + ip = [mutStringSet firstObject]; // expected-warning{{from 'NSString *'}} + ip = [widgetSet firstObject]; // expected-warning{{from 'Widget *'}} + ip = [untypedMutSet firstObject]; // expected-warning{{from 'id'}} + ip = [mutStringArraySet firstObject]; // expected-warning{{from 'NSArray<NSString *> *'}} + ip = [set firstObject]; // expected-warning{{from 'id'}} + ip = [mutSet firstObject]; // expected-warning{{from 'id'}} + ip = [mutArraySet firstObject]; // expected-warning{{from 'id'}} + ip = [block firstObject]; // expected-warning{{from 'id'}} + + ip = [stringSet findObject:@"blah"]; // expected-warning{{from 'NSString *'}} + + // Class messages. + ip = [NSSet<NSString *> alloc]; // expected-warning{{from 'NSSet<NSString *> *'}} + ip = [NSSet alloc]; // expected-warning{{from 'NSSet *'}} + ip = [MutableSetOfArrays<NSString *> alloc]; // expected-warning{{from 'MutableSetOfArrays<NSString *> *'}} + ip = [MutableSetOfArrays alloc]; // expected-warning{{from 'MutableSetOfArrays *'}} + ip = [NSArray<NSString *> array]; // expected-warning{{from 'NSArray<NSString *> *'}} + ip = [NSArray<NSString *><NSCopying> array]; // expected-warning{{from 'NSArray<NSString *> *'}} + + ip = [[NSMutableArray<NSString *> alloc] init]; // expected-warning{{from 'NSMutableArray<NSString *> *'}} + + [[NSMutableArray alloc] initWithArray: stringArray]; // okay + [[NSMutableArray<NSString *> alloc] initWithArray: stringArray]; // okay + [[NSMutableArray<NSNumber *> alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray<NSString *> *' to parameter of type 'NSArray<NSNumber *> *'}} +} + +void test_message_send_param( + NSMutableSet<NSString *> *mutStringSet, + WidgetSet *widgetSet, + UntypedMutableSet *untypedMutSet, + MutableSetOfArrays<NSString *> *mutStringArraySet, + NSMutableSet *mutSet, + MutableSetOfArrays *mutArraySet, + void (^block)(void)) { + Window *window; + + [mutStringSet addObject: window]; // expected-warning{{parameter of type 'NSString *'}} + [widgetSet addObject: window]; // expected-warning{{parameter of type 'Widget *'}} + [untypedMutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} + [mutStringArraySet addObject: window]; // expected-warning{{parameter of type 'NSArray<NSString *> *'}} + [mutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} + [mutArraySet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} + [block addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} +} + +// -------------------------------------------------------------------------- +// Property accesses. +// -------------------------------------------------------------------------- +void test_property_read( + NSSet<NSString *> *stringSet, + NSMutableSet<NSString *> *mutStringSet, + WidgetSet *widgetSet, + UntypedMutableSet *untypedMutSet, + MutableSetOfArrays<NSString *> *mutStringArraySet, + NSSet *set, + NSMutableSet *mutSet, + MutableSetOfArrays *mutArraySet, + NSMutableDictionary *mutDict) { + int *ip; + ip = stringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}} + ip = mutStringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}} + ip = widgetSet.allObjects; // expected-warning{{from 'NSArray<Widget *> *'}} + ip = untypedMutSet.allObjects; // expected-warning{{from 'NSArray *'}} + ip = mutStringArraySet.allObjects; // expected-warning{{from 'NSArray<NSArray<NSString *> *> *'}} + ip = set.allObjects; // expected-warning{{from 'NSArray *'}} + ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}} + ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}} + + ip = mutDict.someRandomKey; // expected-warning{{from 'id'}} +} + +void test_property_write( + NSMutableSet<NSString *> *mutStringSet, + WidgetSet *widgetSet, + UntypedMutableSet *untypedMutSet, + MutableSetOfArrays<NSString *> *mutStringArraySet, + NSMutableSet *mutSet, + MutableSetOfArrays *mutArraySet, + NSMutableDictionary *mutDict) { + int *ip; + + mutStringSet.allObjects = ip; // expected-warning{{to 'NSArray<NSString *> *'}} + widgetSet.allObjects = ip; // expected-warning{{to 'NSArray<Widget *> *'}} + untypedMutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} + mutStringArraySet.allObjects = ip; // expected-warning{{to 'NSArray<NSArray<NSString *> *> *'}} + mutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} + mutArraySet.allObjects = ip; // expected-warning{{to 'NSArray *'}} + + mutDict.someRandomKey = ip; // expected-warning{{to 'id<NSCopying>'}} +} + +// -------------------------------------------------------------------------- +// Subscripting +// -------------------------------------------------------------------------- +void test_subscripting( + NSArray<NSString *> *stringArray, + NSMutableArray<NSString *> *mutStringArray, + NSArray *array, + NSMutableArray *mutArray, + NSDictionary<NSString *, Widget *> *stringWidgetDict, + NSMutableDictionary<NSString *, Widget *> *mutStringWidgetDict, + NSDictionary *dict, + NSMutableDictionary *mutDict) { + int *ip; + NSString *string; + Widget *widget; + Window *window; + + ip = stringArray[0]; // expected-warning{{from 'NSString *'}} + + ip = mutStringArray[0]; // expected-warning{{from 'NSString *'}} + mutStringArray[0] = ip; // expected-warning{{parameter of type 'NSString *'}} + + ip = array[0]; // expected-warning{{from 'id'}} + + ip = mutArray[0]; // expected-warning{{from 'id'}} + mutArray[0] = ip; // expected-warning{{parameter of type 'id'}} + + ip = stringWidgetDict[string]; // expected-warning{{from 'Widget *'}} + widget = stringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} + + ip = mutStringWidgetDict[string]; // expected-warning{{from 'Widget *'}} + widget = mutStringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} + mutStringWidgetDict[string] = ip; // expected-warning{{to parameter of type 'Widget *'}} + mutStringWidgetDict[widget] = widget; // expected-warning{{to parameter of type 'NSString *'}} + + ip = dict[string]; // expected-warning{{from 'id'}} + + ip = mutDict[string]; // expected-warning{{from 'id'}} + mutDict[string] = ip; // expected-warning{{to parameter of type 'id'}} + + widget = mutDict[window]; + mutDict[window] = widget; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} +} + +// -------------------------------------------------------------------------- +// Instance variable access. +// -------------------------------------------------------------------------- +void test_instance_variable(NSArray<NSString *> *stringArray, + NSArray *array) { + int *ip; + + ip = stringArray->data; // expected-warning{{from 'NSString **'}} + ip = array->data; // expected-warning{{from 'id *'}} +} + +@implementation WindowArray +- (void)testInstanceVariable { + int *ip; + + ip = data; // expected-warning{{from 'Window **'}} +} +@end + +// -------------------------------------------------------------------------- +// Implicit conversions. +// -------------------------------------------------------------------------- +void test_implicit_conversions(NSArray<NSString *> *stringArray, + NSArray<NSNumber *> *numberArray, + NSMutableArray<NSString *> *mutStringArray, + NSArray *array, + NSMutableArray *mutArray) { + // Specialized -> unspecialized (same level) + array = stringArray; + + // Unspecialized -> specialized (same level) + stringArray = array; + + // Specialized -> specialized failure (same level). + stringArray = numberArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSString *> *' from 'NSArray<NSNumber *> *'}} + + // Specialized -> specialized (different levels). + stringArray = mutStringArray; + + // Specialized -> specialized failure (different levels). + numberArray = mutStringArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSNumber *> *' from 'NSMutableArray<NSString *> *'}} + + // Unspecialized -> specialized (different levels). + stringArray = mutArray; + + // Specialized -> unspecialized (different levels). + array = mutStringArray; +} + +// -------------------------------------------------------------------------- +// Ternary operator +// -------------------------------------------------------------------------- +void test_ternary_operator(NSArray<NSString *> *stringArray, + NSArray<NSNumber *> *numberArray, + NSMutableArray<NSString *> *mutStringArray, + NSStringArray *stringArray2, + NSArray *array, + NSMutableArray *mutArray, + int cond) { + int *ip; + id object; + + ip = cond ? stringArray : mutStringArray; // expected-warning{{from 'NSArray<NSString *> *'}} + ip = cond ? mutStringArray : stringArray; // expected-warning{{from 'NSArray<NSString *> *'}} + + ip = cond ? stringArray2 : mutStringArray; // expected-warning{{from 'NSArray<NSString *><NSCopying> *'}} + ip = cond ? mutStringArray : stringArray2; // expected-warning{{from 'NSArray<NSString *><NSCopying> *'}} + + ip = cond ? stringArray : mutArray; // FIXME: expected-warning{{from 'NSArray<NSString *> *'}} + + object = cond ? stringArray : numberArray; // expected-warning{{incompatible operand types ('NSArray<NSString *> *' and 'NSArray<NSNumber *> *')}} +} + +// -------------------------------------------------------------------------- +// super +// -------------------------------------------------------------------------- +@implementation NSStringArray +- (void)useSuperMethod { + int *ip; + ip = super.lastObject; // expected-warning{{from 'NSString *'}} + super.lastObject = ip; // expected-warning{{to 'NSString *'}} + ip = [super objectAtIndexedSubscript:0]; // expected-warning{{from 'NSString *'}} +} + ++ (void)useSuperMethod { + int *ip; + ip = super.array; // expected-warning{{from 'NSArray<NSString *> *'}} + super.array = ip; // expected-warning{{to 'NSArray<NSString *> *'}} + ip = [super array]; // expected-warning{{from 'NSArray<NSString *> *'}} +} +@end diff --git a/clang/test/SemaObjCXX/Inputs/nullability-pragmas-generics-1.h b/clang/test/SemaObjCXX/Inputs/nullability-pragmas-generics-1.h new file mode 100644 index 00000000000..9a51fa1e990 --- /dev/null +++ b/clang/test/SemaObjCXX/Inputs/nullability-pragmas-generics-1.h @@ -0,0 +1,21 @@ +#pragma clang assume_nonnull begin + +__attribute__((objc_root_class)) +@interface B +@end + +@interface C : B +@end + +__attribute__((objc_root_class)) +@interface NSGeneric<T : B *> // expected-note{{type parameter 'T' declared here}} +- (T)tee; +- (nullable T)maybeTee; +@end + +typedef NSGeneric<C *> *Generic_with_C; + +#pragma clang assume_nonnull end + +@interface NSGeneric<T : C *>(Blah) // expected-error{{type bound 'C *' for type parameter 'T' conflicts with previous bound 'B *'}} +@end diff --git a/clang/test/SemaObjCXX/nullability-pragmas.mm b/clang/test/SemaObjCXX/nullability-pragmas.mm index dbf4f37f401..817d056a14a 100644 --- a/clang/test/SemaObjCXX/nullability-pragmas.mm +++ b/clang/test/SemaObjCXX/nullability-pragmas.mm @@ -2,6 +2,7 @@ #include "nullability-pragmas-1.h" #include "nullability-pragmas-2.h" +#include "nullability-pragmas-generics-1.h" #if !__has_feature(assume_nonnull) # error assume_nonnull feature is not set @@ -43,3 +44,14 @@ void test_pragmas_1(A * _Nonnull a, AA * _Nonnull aa) { ptr = aa->ivar1; // expected-error{{from incompatible type 'id'}} ptr = aa->ivar2; // expected-error{{from incompatible type 'id _Nonnull'}} } + +void test_pragmas_generics(void) { + float *fp; + + NSGeneric<C *> *genC; + fp = [genC tee]; // expected-error{{from incompatible type 'C *'}} + fp = [genC maybeTee]; // expected-error{{from incompatible type 'C * _Nullable'}} + + Generic_with_C genC2; + fp = genC2; // expected-error{{from incompatible type 'Generic_with_C' (aka 'NSGeneric<C *> *')}} +} |