llvm-project/clang/test/SemaObjC/parameterized_classes_subst.m

379 lines
15 KiB
Mathematica
Raw Normal View History

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
2015-07-07 11:57:53 +08:00
// 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 *> *'}}
ip = cond ? mutStringArray : stringArray2; // expected-warning{{from 'NSArray<NSString *> *'}}
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
2015-07-07 11:57:53 +08:00
ip = cond ? stringArray : mutArray; // expected-warning{{from 'NSArray *'}}
ip = cond ? stringArray2 : mutArray; // expected-warning{{from 'NSArray *'}}
ip = cond ? mutArray : stringArray; // expected-warning{{from 'NSArray *'}}
ip = cond ? mutArray : stringArray2; // expected-warning{{from 'NSArray *'}}
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
2015-07-07 11:57:53 +08:00
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