Skip to content

Commit 694eebf

Browse files
committed
Match @objcImpl member types
Check the types of @objcImpl candidates against their requirements and diagnose *most* mismatches. Unlike typical type matching rules, @objcImpl allows an implementation’s parameter *and* result types to be an IUO when the requirement isn’t. This runs against the normal covariance rules in the case of the result type. It’s meant to allow an implementation to handle nils passed to it and return nils even if the declaration formally claims nils are not permitted; this is occasionally necessary to reimplement Objective-C APIs without breaking ABI compatibility. Fixes rdar://102063730.
1 parent 1f297fd commit 694eebf

File tree

6 files changed

+155
-9
lines changed

6 files changed

+155
-9
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,11 @@ ERROR(objc_implementation_must_be_settable,none,
16331633
"header",
16341634
(DescriptiveDeclKind, ValueDecl *, DescriptiveDeclKind))
16351635

1636+
ERROR(objc_implementation_type_mismatch,none,
1637+
"%0 %1 of type %2 does not match type %3 declared by the "
1638+
"header",
1639+
(DescriptiveDeclKind, ValueDecl *, Type, Type))
1640+
16361641
ERROR(objc_implementation_wrong_objc_name,none,
16371642
"selector %0 for %1 %2 not found in header; did you mean %3?",
16381643
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3020,7 +3020,7 @@ class ObjCImplementationChecker {
30203020
WrongStaticness,
30213021
WrongCategory,
30223022
WrongDeclKind,
3023-
// WrongType,
3023+
WrongType,
30243024
WrongWritability,
30253025

30263026
Match,
@@ -3213,6 +3213,71 @@ class ObjCImplementationChecker {
32133213
return lhs == rhs;
32143214
}
32153215

3216+
static bool matchParamTypes(Type reqTy, Type implTy, ValueDecl *implDecl) {
3217+
TypeMatchOptions matchOpts = {};
3218+
3219+
// Try a plain type match.
3220+
if (implTy->matchesParameter(reqTy, matchOpts))
3221+
return true;
3222+
3223+
// If the implementation type is IUO, try unwrapping it.
3224+
if (auto unwrappedImplTy = implTy->getOptionalObjectType())
3225+
return implDecl->isImplicitlyUnwrappedOptional()
3226+
&& unwrappedImplTy->matchesParameter(reqTy, matchOpts);
3227+
3228+
return false;
3229+
}
3230+
3231+
static bool matchTypes(Type reqTy, Type implTy, ValueDecl *implDecl) {
3232+
TypeMatchOptions matchOpts = {};
3233+
3234+
// Try a plain type match.
3235+
if (reqTy->matches(implTy, matchOpts))
3236+
return true;
3237+
3238+
// If the implementation type is optional, try unwrapping it.
3239+
if (auto unwrappedImplTy = implTy->getOptionalObjectType())
3240+
return implDecl->isImplicitlyUnwrappedOptional()
3241+
&& reqTy->matches(unwrappedImplTy, matchOpts);
3242+
3243+
// Apply these rules to the result type and parameters if it's a function
3244+
// type.
3245+
if (auto funcReqTy = reqTy->getAs<AnyFunctionType>())
3246+
if (auto funcImplTy = implTy->getAs<AnyFunctionType>())
3247+
return funcReqTy->matchesFunctionType(funcImplTy, matchOpts,
3248+
[=]() -> bool {
3249+
auto reqParams = funcReqTy->getParams();
3250+
auto implParams = funcImplTy->getParams();
3251+
if (reqParams.size() != implParams.size())
3252+
return false;
3253+
3254+
auto implParamList =
3255+
cast<AbstractFunctionDecl>(implDecl)->getParameters();
3256+
3257+
for (auto i : indices(reqParams)) {
3258+
const auto &reqParam = reqParams[i];
3259+
const auto &implParam = implParams[i];
3260+
ParamDecl *implParamDecl = implParamList->get(i);
3261+
3262+
if (!matchParamTypes(reqParam.getOldType(), implParam.getOldType(),
3263+
implParamDecl))
3264+
return false;
3265+
}
3266+
3267+
return matchTypes(funcReqTy->getResult(), funcImplTy->getResult(),
3268+
implDecl);
3269+
});
3270+
3271+
return false;
3272+
}
3273+
3274+
static Type getMemberType(ValueDecl *decl) {
3275+
if (isa<AbstractFunctionDecl>(decl))
3276+
// Strip off the uncurried `self` parameter.
3277+
return decl->getInterfaceType()->getAs<AnyFunctionType>()->getResult();
3278+
return decl->getInterfaceType();
3279+
}
3280+
32163281
MatchOutcome matchesImpl(ValueDecl *req, ValueDecl *cand,
32173282
ObjCSelector explicitObjCName) const {
32183283
bool hasObjCNameMatch =
@@ -3246,7 +3311,8 @@ class ObjCImplementationChecker {
32463311
if (cand->getKind() != req->getKind())
32473312
return MatchOutcome::WrongDeclKind;
32483313

3249-
// FIXME: Diagnose type mismatches (with allowance for extra optionality)
3314+
if (!matchTypes(getMemberType(req), getMemberType(cand), cand))
3315+
return MatchOutcome::WrongType;
32503316

32513317
if (auto reqVar = dyn_cast<AbstractStorageDecl>(req))
32523318
if (reqVar->isSettable(nullptr) &&
@@ -3330,6 +3396,12 @@ class ObjCImplementationChecker {
33303396
cand->getDescriptiveKind(), cand, req->getDescriptiveKind());
33313397
return;
33323398

3399+
case MatchOutcome::WrongType:
3400+
diagnose(cand, diag::objc_implementation_type_mismatch,
3401+
cand->getDescriptiveKind(), cand,
3402+
getMemberType(cand), getMemberType(req));
3403+
return;
3404+
33333405
case MatchOutcome::WrongWritability:
33343406
diagnose(cand, diag::objc_implementation_must_be_settable,
33353407
cand->getDescriptiveKind(), cand, req->getDescriptiveKind());

test/IRGen/objc_implementation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
// CHECK: [[selector_data_implProperty:@[^, ]+]] = private global [13 x i8] c"implProperty\00", section "__TEXT,__objc_methname,cstring_literals", align 1
2323
// CHECK: [[selector_data_setImplProperty_:@[^, ]+]] = private global [17 x i8] c"setImplProperty:\00", section "__TEXT,__objc_methname,cstring_literals", align 1
2424
// CHECK: [[selector_data__cxx_destruct:@[^, ]+]] = private global [14 x i8] c".cxx_destruct\00", section "__TEXT,__objc_methname,cstring_literals", align 1
25-
// CHECK: [[_INSTANCE_METHODS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [7 x { i8*, i8*, i8* }] } { i32 24, i32 7, [7 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01L_selector_data(init)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEABycfcTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[selector_data_implProperty]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.i16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvgTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[selector_data_setImplProperty_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvsTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"\01L_selector_data(copyWithZone:)", i64 0, i64 0), i8* getelementptr inbounds ([12 x i8], [12 x i8]* @".str.11.@24@0:8^v16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE4copy4withyp10ObjectiveC6NSZoneV_tFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([8 x i8], [8 x i8]* @"\01L_selector_data(dealloc)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEfDTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[selector_data__cxx_destruct]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassCfETo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8
25+
// CHECK: [[_INSTANCE_METHODS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [7 x { i8*, i8*, i8* }] } { i32 24, i32 7, [7 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01L_selector_data(init)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEABycfcTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[selector_data_implProperty]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.i16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvgTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[selector_data_setImplProperty_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvsTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"\01L_selector_data(copyWithZone:)", i64 0, i64 0), i8* getelementptr inbounds ([12 x i8], [12 x i8]* @".str.11.@24@0:8^v16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE4copy4withypSg10ObjectiveC6NSZoneVSg_tFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([8 x i8], [8 x i8]* @"\01L_selector_data(dealloc)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEfDTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[selector_data__cxx_destruct]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassCfETo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8
2626
// CHECK: [[_IVARS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] } { i32 32, i32 2, [2 x { i64*, i8*, i8*, i32, i32 }] [{ i64*, i8*, i8*, i32, i32 } { i64* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvpWvd", i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.12.implProperty, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 2, i32 4 }, { i64*, i8*, i8*, i32, i32 } { i64* @"$sSo9ImplClassC19objc_implementationE13implProperty2So8NSObjectCSgvpWvd", i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.13.implProperty2, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 8 }] }, section "__DATA, __objc_const", align 8
2727
// CHECK: [[_PROPERTIES_ImplClass:@[^, ]+]] = internal constant { i32, i32, [1 x { i8*, i8* }] } { i32 16, i32 1, [1 x { i8*, i8* }] [{ i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.12.implProperty, i64 0, i64 0), i8* getelementptr inbounds ([19 x i8], [19 x i8]* @".str.18.Ti,N,VimplProperty", i64 0, i64 0) }] }, section "__DATA, __objc_const", align 8
2828
// FIXME: Should just be @_PROTOCOLS_ImplClass, but an IRGen bug causes the protocol list to be emitted twice.
@@ -51,7 +51,7 @@
5151
@objc func mainMethod(_: Int32) { print(implProperty) }
5252

5353
@objc(copyWithZone:)
54-
func copy(with zone: NSZone) -> Any {
54+
func copy(with zone: NSZone?) -> Any? {
5555
let copy = ImplClass()
5656
copy.implProperty = implProperty
5757
copy.implProperty2 = implProperty2
@@ -191,10 +191,10 @@ public func fn(impl: ImplClass, swiftSub: SwiftSubclass) {
191191
// CHECK-LABEL: define internal void @"$sSo9ImplClassC19objc_implementationE10mainMethodyys5Int32VFTo"
192192

193193
// Swift calling convention -[ImplClass copyWithZone:]
194-
// CHECK-LABEL: define hidden swiftcc void @"$sSo9ImplClassC19objc_implementationE4copy4withyp10ObjectiveC6NSZoneV_tF"
194+
// CHECK-LABEL: define hidden swiftcc void @"$sSo9ImplClassC19objc_implementationE4copy4withypSg10ObjectiveC6NSZoneVSg_tF"
195195

196196
// ObjC calling convention -[ImplClass copyWithZone:]
197-
// CHECK-LABEL: define internal i8* @"$sSo9ImplClassC19objc_implementationE4copy4withyp10ObjectiveC6NSZoneV_tFTo"
197+
// CHECK-LABEL: define internal i8* @"$sSo9ImplClassC19objc_implementationE4copy4withypSg10ObjectiveC6NSZoneVSg_tFTo"
198198

199199
// Swift calling convention -[ImplClass dealloc]
200200
// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$sSo9ImplClassC19objc_implementationEfD"

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- (void)methodFromHeader3:(int)param;
2020
- (void)methodFromHeader4:(int)param;
2121
- (int)methodFromHeader5;
22+
- (void)methodFromHeader6:(int)param;
2223

2324
@property int propertyFromHeader1;
2425
@property int propertyFromHeader2;
@@ -30,6 +31,7 @@
3031
@property int propertyFromHeader8;
3132
@property int propertyFromHeader9;
3233
@property int propertyFromHeader10;
34+
@property int propertyFromHeader11;
3335

3436
@property (readonly) int readonlyPropertyFromHeader1;
3537
@property (readonly) int readonlyPropertyFromHeader2;
@@ -40,6 +42,7 @@
4042

4143
+ (void)classMethod1:(int)param;
4244
+ (void)classMethod2:(int)param;
45+
+ (void)classMethod3:(int)param;
4346

4447
- (void)instanceMethod1:(int)param;
4548
- (void)instanceMethod2:(int)param;
@@ -108,6 +111,28 @@
108111

109112
@end
110113

114+
@interface ObjCClass (TypeMatchOptionality)
115+
116+
- (nullable id)nullableResultAndArg:(nullable id)arg;
117+
- (nonnull id)nonnullResultAndArg:(nonnull id)arg;
118+
- (null_unspecified id)nullUnspecifiedResultAndArg:(null_unspecified id)arg;
119+
120+
- (nonnull id)nonnullResult1;
121+
- (nonnull id)nonnullResult2;
122+
- (nonnull id)nonnullResult3;
123+
124+
- (void)nonnullArgument1:(nonnull id)arg;
125+
- (void)nonnullArgument2:(nonnull id)arg;
126+
- (void)nonnullArgument3:(nonnull id)arg;
127+
128+
- (nullable id)nullableResult;
129+
- (void)nullableArgument:(nullable id)arg;
130+
131+
- (int)nonPointerResult;
132+
- (void)nonPointerArgument:(int)arg;
133+
134+
@end
135+
111136
@interface ObjCSubclass : ObjCClass
112137

113138
- (void)subclassMethodFromHeader1:(int)param;

test/decl/ext/objc_implementation.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
var methodFromHeader5: CInt
4242
// expected-error@-1 {{property 'methodFromHeader5' does not match the instance method declared by the header}}
4343

44+
func method(fromHeader6: Double) {
45+
// expected-error@-1 {{instance method 'method(fromHeader6:)' of type '(Double) -> ()' does not match type '(Int32) -> Void' declared by the header}}
46+
}
47+
4448
var propertyFromHeader1: CInt
4549
// OK, provides an implementation with a stored property
4650

@@ -75,6 +79,9 @@
7579
return 1
7680
}
7781

82+
var propertyFromHeader11: Float
83+
// expected-error@-1 {{property 'propertyFromHeader11' of type 'Float' does not match type 'Int32' declared by the header}}
84+
7885
var readonlyPropertyFromHeader1: CInt
7986
// OK, provides an implementation with a stored property that's nonpublicly settable
8087

@@ -146,6 +153,10 @@
146153
// expected-warning@-1 {{instance method 'classMethod2' does not match class method declared in header; this will become an error before '@_objcImplementation' is stabilized}} {{3-3=class }}
147154
}
148155

156+
class func classMethod3(_: Float) {
157+
// expected-error@-1 {{class method 'classMethod3' of type '(Float) -> ()' does not match type '(Int32) -> Void' declared by the header}}
158+
}
159+
149160
func instanceMethod1(_: CInt) {
150161
// OK
151162
}
@@ -308,6 +319,26 @@
308319
func optionalMethod1() {}
309320
}
310321

322+
@_objcImplementation(TypeMatchOptionality) extension ObjCClass {
323+
func nullableResultAndArg(_: Any?) -> Any? { fatalError() } // no-error
324+
func nonnullResultAndArg(_: Any) -> Any { fatalError() } // no-error
325+
func nullUnspecifiedResultAndArg(_: Any!) -> Any! { fatalError() } // no-error
326+
327+
func nonnullResult1() -> Any { fatalError() } // no-error
328+
func nonnullResult2() -> Any? { fatalError() } // expected-error {{instance method 'nonnullResult2()' of type '() -> Any?' does not match type '() -> Any' declared by the header}}
329+
func nonnullResult3() -> Any! { fatalError() } // no-error (special case)
330+
331+
func nonnullArgument1(_: Any) {} // no-error
332+
func nonnullArgument2(_: Any?) {} // expected-error {{instance method 'nonnullArgument2' of type '(Any?) -> ()' does not match type '(Any) -> Void' declared by the header}}
333+
func nonnullArgument3(_: Any!) {} // no-error (special case)
334+
335+
func nullableResult() -> Any { fatalError() } // expected-error {{instance method 'nullableResult()' of type '() -> Any' does not match type '() -> Any?' declared by the header}}
336+
func nullableArgument(_: Any) {} // expected-error {{instance method 'nullableArgument' of type '(Any) -> ()' does not match type '(Any?) -> Void' declared by the header}}
337+
338+
func nonPointerResult() -> CInt! { fatalError() } // expected-error{{method cannot be implicitly @objc because its result type cannot be represented in Objective-C}}
339+
func nonPointerArgument(_: CInt!) {} // expected-error {{method cannot be implicitly @objc because the type of the parameter cannot be represented in Objective-C}}
340+
}
341+
311342
@_objcImplementation extension ObjCClass {}
312343
// expected-error@-1 {{duplicate implementation of Objective-C class 'ObjCClass'}}
313344

test/decl/ext/objc_implementation_conflicts.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ import objc_implementation_private
2828
// OK
2929
}
3030

31+
func methodFromHeader5() -> CInt {
32+
return 1 // OK
33+
}
34+
35+
func method(fromHeader6: CInt) {
36+
// OK
37+
}
38+
3139
@objc fileprivate func methodNot(fromHeader1: CInt) {
3240
// OK, declares a new @objc dynamic method.
3341
}
@@ -54,22 +62,26 @@ import objc_implementation_private
5462
set {}
5563
}
5664

57-
@objc let propertyFromHeader5: CInt
58-
// FIXME: bad, needs to be settable
65+
@objc var propertyFromHeader5: CInt
5966

6067
@objc var propertyFromHeader6: CInt {
61-
// FIXME: bad, needs a setter
6268
get { return 1 }
69+
set {}
6370
}
6471

6572
@objc var propertyFromHeader7: CInt {
6673
get { return 1 }
74+
set {}
6775
}
6876

6977
var propertyFromHeader8: CInt
7078

7179
@objc var propertyFromHeader9: CInt
7280

81+
@objc var propertyFromHeader10: CInt
82+
83+
@objc var propertyFromHeader11: CInt
84+
7385
@objc var readonlyPropertyFromHeader1: CInt
7486
// OK, provides an implementation with a stored property that's nonpublicly settable
7587

@@ -136,6 +148,7 @@ import objc_implementation_private
136148

137149
class func classMethod1(_: CInt) {}
138150
class func classMethod2(_: CInt) {}
151+
class func classMethod3(_: CInt) {}
139152

140153
func instanceMethod1(_: CInt) {}
141154
func instanceMethod2(_: CInt) {}

0 commit comments

Comments
 (0)