#if !__has_feature(objc_generics)
#  error Compiler does not support Objective-C generics?
#endif
#if !__has_feature(objc_generics_variance)
#  error Compiler does not support co- and contr-variance?
#endif
#define nil 0
typedef unsigned long NSUInteger;
typedef int BOOL;
@protocol NSObject
+ (id)alloc;
- (id)init;
@end
@protocol NSCopying
@end
__attribute__((objc_root_class))
@interface NSObject <NSObject>
@end
@interface NSString : NSObject <NSCopying>
@end
@interface NSMutableString : NSString
@end
@interface NSNumber : NSObject <NSCopying>
@end
@interface NSSet : NSObject <NSCopying>
@end
@interface NSArray<__covariant ObjectType> : NSObject
+ (instancetype)arrayWithObjects:(const ObjectType [])objects count:(NSUInteger)count;
+ (instancetype)getEmpty;
+ (NSArray<ObjectType> *)getEmpty2;
- (BOOL)contains:(ObjectType)obj;
- (BOOL)containsObject:(ObjectType)anObject;
- (ObjectType)getObjAtIndex:(NSUInteger)idx;
- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
- (NSArray<ObjectType> *)arrayByAddingObject:(ObjectType)anObject;
@property(readonly) ObjectType firstObject;
@end
@interface NSMutableArray<ObjectType> : NSArray<ObjectType>
- (void)addObject:(ObjectType)anObject;
- (instancetype)init;
@end
@interface MutableArray<ObjectType> : NSArray<ObjectType>
- (void)addObject:(ObjectType)anObject;
@end
@interface LegacyMutableArray : MutableArray
@end
@interface LegacySpecialMutableArray : LegacyMutableArray
@end
@interface BuggyMutableArray<T> : MutableArray
@end
@interface BuggySpecialMutableArray<T> : BuggyMutableArray<T>
@end
@interface MyMutableStringArray : MutableArray<NSString *>
@end
@interface ExceptionalArray<ExceptionType> : MutableArray<NSString *>
- (ExceptionType) getException;
@end
@interface UnrelatedType : NSObject<NSCopying>
@end
int getUnknown(void);
NSArray *getStuff(void);
NSArray *getTypedStuff(void) {
  NSArray<NSNumber *> *c = getStuff();
  return c;
}
void doStuff(NSArray<NSNumber *> *);
void withArrString(NSArray<NSString *> *);
void withArrMutableString(NSArray<NSMutableString *> *);
void withMutArrString(MutableArray<NSString *> *);
void withMutArrMutableString(MutableArray<NSMutableString *> *);
void incompatibleTypesErased(NSArray *a, NSMutableArray<NSString *> *b,
                             NSArray<NSNumber *> *c,
                             NSMutableArray *d) {
  a = b;
  c = a;   [a contains: [[NSNumber alloc] init]];
  [a contains: [[NSString alloc] init]];
  doStuff(a); 
  d = b;
  [d addObject: [[NSNumber alloc] init]]; }
void crossProceduralErasedTypes(void) {
  NSArray<NSString *> *a = getTypedStuff(); }
void incompatibleTypesErasedReverseConversion(NSMutableArray *a,
                                              NSMutableArray<NSString *> *b) {
  b = a;
  [a contains: [[NSNumber alloc] init]];
  [a contains: [[NSString alloc] init]];
  doStuff(a); 
  [a addObject: [[NSNumber alloc] init]]; }
void idErasedIncompatibleTypesReverseConversion(id a, NSMutableArray<NSString *> *b) {
  b = a;
  [a contains: [[NSNumber alloc] init]];
  [a contains: [[NSString alloc] init]];
  doStuff(a); 
  [a addObject:[[NSNumber alloc] init]]; }
void idErasedIncompatibleTypes(id a, NSMutableArray<NSString *> *b,
                               NSArray<NSNumber *> *c) {
  a = b;
  c = a;   [a contains: [[NSNumber alloc] init]];
  [a contains: [[NSString alloc] init]];
  doStuff(a); 
  [a addObject:[[NSNumber alloc] init]]; }
void pathSensitiveInference(MutableArray *m, MutableArray<NSString *> *a,
                            MutableArray<NSMutableString *> *b) {
  if (getUnknown() == 5) {
    m = a;  
    [m contains: [[NSString alloc] init]];
  } else {
    m = b;
    [m contains: [[NSMutableString alloc] init]];
  }
  [m addObject: [[NSString alloc] init]];   [m addObject: [[NSMutableString alloc] init]];
}
void verifyAPIusage(id a, MutableArray<NSString *> *b) {
  b = a;
  doStuff(a); }
void dontInferFromExplicitCastsOnUnspecialized(MutableArray *a,
                        MutableArray<NSMutableString *> *b) {
  b = (MutableArray<NSMutableString *> *)a;
  [a addObject: [[NSString alloc] init]]; }
void dontWarnOnExplicitCastsAfterInference(MutableArray *a) {
  withMutArrString(a);
  withMutArrMutableString((MutableArray<NSMutableString *> *)a); }
void dontDiagnoseOnExplicitCrossCasts(MutableArray<NSSet *> *a,
                        MutableArray<NSMutableString *> *b) {
          b = (MutableArray<NSMutableString *> *)a;   [a addObject: [[NSSet alloc] init]];   [b addObject: [[NSMutableString alloc] init]]; }
void subtypeOfGeneric(id d, MyMutableStringArray *a,
                       MutableArray<NSString *> *b,
                       MutableArray<NSNumber *> *c) {
  d = a;
  b = d;
  c = d; }
void genericSubtypeOfGeneric(id d, ExceptionalArray<NSString *> *a,
                             MutableArray<NSString *> *b,
                             MutableArray<NSNumber *> *c) {
  d = a;
  [d contains: [[NSString alloc] init]];
  [d contains: [[NSNumber alloc] init]];
  b = d;
  c = d; 
  [d addObject: [[NSNumber alloc] init]]; }
void genericSubtypeOfGenericReverse(id d, ExceptionalArray<NSString *> *a,
                                    MutableArray<NSString *> *b,
                                    MutableArray<NSNumber *> *c) {
  a = d;
  [d contains: [[NSString alloc] init]];
  [d contains: [[NSNumber alloc] init]];
  b = d;
  c = d; 
 [d addObject: [[NSNumber alloc] init]]; }
void inferenceFromAPI(id a) {
      withMutArrString(a);
  withMutArrMutableString(a); }
void inferenceFromAPI2(id a) {
  withMutArrMutableString(a);
  withMutArrString(a); }
void inferenceFromAPIWithLegacyTypes(LegacyMutableArray *a) {
  withMutArrMutableString(a);
  withMutArrString(a); }
void inferenceFromAPIWithLegacyTypes2(LegacySpecialMutableArray *a) {
  withMutArrString(a);
  withMutArrMutableString(a); }
void inferenceFromAPIWithLegacyTypes3(__kindof NSArray<NSString *> *a) {
  LegacyMutableArray *b = a;
  withMutArrString(b);
  withMutArrMutableString(b); }
void inferenceFromAPIWithBuggyTypes(BuggyMutableArray<NSMutableString *> *a) {
  withMutArrString(a);
  withMutArrMutableString(a); }
void InferenceFromAPIWithBuggyTypes2(BuggySpecialMutableArray<NSMutableString *> *a) {
  withMutArrMutableString(a);
  withMutArrString(a); }
void InferenceFromAPIWithBuggyTypes3(MutableArray<NSMutableString *> *a) {
  id b = a;
  withMutArrMutableString((BuggyMutableArray<NSMutableString *> *)b);
  withMutArrString(b); }
void InferenceFromAPIWithBuggyTypes4(__kindof NSArray<NSString *> *a) {
  BuggyMutableArray<NSMutableString *> *b = a;
  withMutArrString(b);
  withMutArrMutableString(b); }
NSArray<NSString *> *getStrings(void);
void enforceDynamicRulesInsteadOfStatic(NSArray<NSNumber *> *a) {
  NSArray *b = a;
    b = getStrings();
  }
void workWithProperties(NSArray<NSNumber *> *a) {
  NSArray *b = a;
  NSString *str = [b getObjAtIndex: 0];   NSNumber *num = [b getObjAtIndex: 0];
  str = [b firstObject];   num = [b firstObject];
  str = b.firstObject;   num = b.firstObject;
  str = b[0];   num = b[0];
}
void findMethodDeclInTrackedType(id m, NSArray<NSMutableString *> *a,
                                 MutableArray<NSMutableString *> *b) {
  a = b;
  if (getUnknown() == 5) {
    m = a;  
    [m addObject: [[NSString alloc] init]];   } else {
    m = b;
    [m addObject: [[NSMutableString alloc] init]];
  }
}
void findMethodDeclInTrackedType2(__kindof NSArray<NSString *> *a,
                                  MutableArray<NSMutableString *> *b) {
  a = b;
  if (getUnknown() == 5) {
    [a addObject: [[NSString alloc] init]];   } else {
    [a addObject: [[NSMutableString alloc] init]];
  }
}
void testUnannotatedLiterals(void) {
    NSArray *arr = @[@"A", @"B"];
  [arr contains: [[NSNumber alloc] init]];
}
void testAnnotatedLiterals(void) {
  NSArray<NSString *> *arr = @[@"A", @"B"];
  NSArray *arr2 = arr;
  [arr2 contains: [[NSNumber alloc] init]];
}
void nonExistentMethodDoesNotCrash(id a, MutableArray<NSMutableString *> *b) {
  a = b;
  [a nonExistentMethod];
}
void trackedClassVariables(void) {
  Class c = [NSArray<NSString *> class];
  NSArray<NSNumber *> *a = [c getEmpty];   a = [c getEmpty2]; }
void nestedCollections(NSArray<NSArray<NSNumber *> *> *mat, NSArray<NSString *> *row) {
  id temp = row;
  [mat contains: temp]; }
void testMistmatchedTypeCast(MutableArray<NSMutableString *> *a) {
  MutableArray *b = (MutableArray<NSNumber *> *)a;
  [b addObject: [[NSNumber alloc] init]];
  id c = (UnrelatedType *)a;
  [c addObject: [[NSNumber alloc] init]];
  [c addObject: [[NSString alloc] init]];
}
void returnCollectionToIdVariable(NSArray<NSArray<NSString *> *> *arr) {
  NSArray *erased = arr;
  id a = [erased firstObject];
  NSArray<NSNumber *> *res = a; }
void eraseSpecialization(NSArray<NSArray<NSString *> *> *arr) {
  NSArray *erased = arr;
  NSArray* a = [erased firstObject];
  NSArray<NSNumber *> *res = a; }
void returnToUnrelatedType(NSArray<NSArray<NSString *> *> *arr) {
  NSArray *erased = arr;
  NSSet* a = [erased firstObject];   (void)a;
}
void returnToIdVariable(NSArray<NSString *> *arr) {
  NSArray *erased = arr;
  id a = [erased firstObject];
  NSNumber *res = a; }
@interface UnrelatedTypeGeneric<T> : NSObject<NSCopying>
- (void)takesType:(T)v;
@end
void testGetMostInformativeDerivedForId(NSArray<NSString *> *a,
                                  UnrelatedTypeGeneric<NSString *> *b) {
  id idB = b;
  a = idB; 
      id x = a;   [x takesType:[[NSNumber alloc] init]]; }
void testArgumentAfterUpcastToRootWithCovariantTypeParameter(NSArray<NSString *> *allStrings, NSNumber *number) {
  NSArray<NSObject *> *allObjects = allStrings;   NSArray<NSObject *> *moreObjects = [allObjects arrayByAddingObject:number]; }
void testArgumentAfterUpcastWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) {
  NSArray<NSString *> *allStrings = allMutableStrings;   id numberAsId = number;
  NSArray<NSString *> *moreStrings = [allStrings arrayByAddingObject:numberAsId]; }
void testArgumentAfterCastToUnspecializedWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) {
  NSArray *allStrings = allMutableStrings;   id numberAsId = number;
  NSArray *moreStringsUnspecialized = [allStrings arrayByAddingObject:numberAsId]; 
    NSArray<NSString *> *moreStringsSpecialized = [allStrings arrayByAddingObject:numberAsId];
}
void testCallToMethodWithCovariantParameterOnInstanceOfSubclassWithInvariantParameter(NSMutableArray<NSMutableString *> *mutableArrayOfMutableStrings, NSString *someString) {
  NSArray<NSString *> *arrayOfStrings = mutableArrayOfMutableStrings;
  [arrayOfStrings containsObject:someString]; }