Skip to content

Commit 0dd1311

Browse files
authored
Merge pull request #41650 from slavapestov/rqm-inferred-signatures-on-by-default
RequirementMachine: Enable -requirement-machine-inferred-signatures=verify by default
2 parents 422c87a + 2206b1c commit 0dd1311

8 files changed

+218
-75
lines changed

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,59 @@ static bool isCanonicalRequest(GenericSignature baseSignature,
243243
return true;
244244
}
245245

246+
/// Hack for GenericSignatureBuilder compatibility. We might end up with a
247+
/// same-type requirement between type parameters where one of them has an
248+
/// implied concrete type requirement. In this case, split it up into two
249+
/// concrete type requirements.
250+
static bool shouldSplitConcreteEquivalenceClass(Requirement req,
251+
GenericSignature sig) {
252+
return (req.getKind() == RequirementKind::SameType &&
253+
req.getSecondType()->isTypeParameter() &&
254+
sig->isConcreteType(req.getSecondType()));
255+
}
256+
257+
static bool shouldSplitConcreteEquivalenceClasses(GenericSignature sig) {
258+
for (auto req : sig.getRequirements()) {
259+
if (shouldSplitConcreteEquivalenceClass(req, sig))
260+
return true;
261+
}
262+
263+
return false;
264+
}
265+
266+
static GenericSignature splitConcreteEquivalenceClasses(
267+
GenericSignature sig, ASTContext &ctx) {
268+
SmallVector<Requirement, 2> reqs;
269+
270+
for (auto req : sig.getRequirements()) {
271+
if (shouldSplitConcreteEquivalenceClass(req, sig)) {
272+
auto canType = sig->getSugaredType(
273+
sig.getCanonicalTypeInContext(
274+
req.getSecondType()));
275+
276+
reqs.emplace_back(RequirementKind::SameType,
277+
req.getFirstType(),
278+
canType);
279+
reqs.emplace_back(RequirementKind::SameType,
280+
req.getSecondType(),
281+
canType);
282+
} else {
283+
reqs.push_back(req);
284+
}
285+
}
286+
287+
SmallVector<GenericTypeParamType *, 2> genericParams;
288+
genericParams.append(sig.getGenericParams().begin(),
289+
sig.getGenericParams().end());
290+
291+
return evaluateOrDefault(
292+
ctx.evaluator,
293+
AbstractGenericSignatureRequestRQM{
294+
/*baseSignature=*/nullptr,
295+
genericParams, reqs},
296+
GenericSignatureWithError()).getPointer();
297+
}
298+
246299
GenericSignatureWithError
247300
AbstractGenericSignatureRequestRQM::evaluate(
248301
Evaluator &evaluator,
@@ -391,8 +444,13 @@ AbstractGenericSignatureRequestRQM::evaluate(
391444
auto result = GenericSignature::get(genericParams, minimalRequirements);
392445
auto errorFlags = machine->getErrors();
393446

394-
if (!errorFlags)
447+
if (!errorFlags) {
448+
if (shouldSplitConcreteEquivalenceClasses(result))
449+
result = splitConcreteEquivalenceClasses(result, ctx);
450+
451+
// Check invariants.
395452
result.verify();
453+
}
396454

397455
return GenericSignatureWithError(result, errorFlags);
398456
}
@@ -543,9 +601,13 @@ InferredGenericSignatureRequestRQM::evaluate(
543601

544602
// FIXME: Handle allowConcreteGenericParams
545603

546-
// Check invariants.
547-
if (!errorFlags)
604+
if (!errorFlags) {
605+
if (shouldSplitConcreteEquivalenceClasses(result))
606+
result = splitConcreteEquivalenceClasses(result, ctx);
607+
608+
// Check invariants.
548609
result.verify();
610+
}
549611

550612
return GenericSignatureWithError(result, errorFlags);
551613
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
913913
Args.hasArg(OPT_disable_subst_sil_function_types);
914914

915915
Opts.RequirementMachineProtocolSignatures = RequirementMachineMode::Verify;
916+
Opts.RequirementMachineInferredSignatures = RequirementMachineMode::Verify;
916917
Opts.RequirementMachineAbstractSignatures = RequirementMachineMode::Verify;
917918

918919
if (auto A = Args.getLastArg(OPT_requirement_machine_protocol_signatures_EQ)) {

test/Generics/rdar90402519.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// This needs a better diagnostic. The real problem is the 'C == G<I>'
4+
// requirement in P2 conflicts with the one in P1.
5+
6+
protocol P {
7+
associatedtype I
8+
}
9+
10+
struct G1<I> : P {}
11+
struct G2<T> : P {
12+
typealias I = G<T>
13+
}
14+
15+
struct G<T> {}
16+
17+
protocol P0 {
18+
associatedtype C : P
19+
}
20+
21+
protocol P1 : P0 where C == G1<I> {
22+
associatedtype I
23+
}
24+
25+
protocol P2 : P1 where C == G2<I> {}
26+
// expected-error@-1 {{cannot build rewrite system for protocol; concrete nesting limit exceeded}}
27+
// expected-note@-2 {{failed rewrite rule is [P2:I].[concrete: G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<G<[P2:I]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => [P2:I]}}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
3+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f01@
4+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Sequence]Element == Character>
5+
func f01<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element {}
6+
7+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f02@
8+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Sequence]Element == Character, R.[Collection]SubSequence == Substring>
9+
func f02<C : Collection, R : Collection>(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element {}
10+
11+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f03@
12+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Sequence]Element == Character>
13+
func f03<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element, C.Element == Character {}
14+
15+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f04@
16+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Sequence]Element == Character, R.[Collection]SubSequence == Substring>
17+
func f04<C : Collection, R : Collection>(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element, C.Element == Character {}
18+
19+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f05@
20+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Sequence]Element == Character>
21+
func f05<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element, R.Element == Character {}
22+
23+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f06@
24+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Sequence]Element == Character, R.[Collection]SubSequence == Substring>
25+
func f06<C : Collection, R : Collection>(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element, R.Element == Character {}
26+
27+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f07@
28+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Collection]SubSequence == Substring>
29+
func f07<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element {}
30+
31+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f08@
32+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Collection]SubSequence == Substring>
33+
func f08<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element, C.Element == Character {}
34+
35+
// CHECK-LABEL: split_concrete_equivalence_class.(file).f09@
36+
// CHECK-NEXT: Generic signature: <C, R where C : Collection, R : Collection, C.[Collection]SubSequence == Substring, R.[Collection]SubSequence == Substring>
37+
func f09<C : Collection, R : Collection>(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element, R.Element == Character {}
Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
// RUN: %target-typecheck-verify-swift -dump-requirement-machine 2>&1 | %FileCheck %s
1+
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s
2+
// RUN: %target-typecheck-verify-swift -dump-requirement-machine -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s --check-prefix=CHECK-RULE
23

34
// Note: The GSB fails this test, because it doesn't implement unification of
45
// superclass type constructor arguments.
56

7+
// FIXME: The Requirement Machine also fails to minimize the signature of
8+
// unifySuperclassTest(). rdar://90469643
9+
610
class Generic<T, U, V> {}
711

812
protocol P1 {
@@ -21,30 +25,32 @@ func sameType<T>(_: T.Type, _: T.Type) {}
2125

2226
func takesGenericIntString<U>(_: Generic<Int, String, U>.Type) {}
2327

28+
// CHECK-LABEL: .unifySuperclassTest@
29+
// CHECK-NEXT: Generic signature: <T where T : P1, T : P2>
2430
func unifySuperclassTest<T : P1 & P2>(_: T) {
2531
sameType(T.A1.self, String.self)
2632
sameType(T.A2.self, Int.self)
2733
sameType(T.B1.self, T.B2.self)
2834
takesGenericIntString(T.X.self)
2935
}
3036

31-
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
32-
// CHECK-NEXT: Rewrite system: {
33-
// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
34-
// CHECK: - τ_0_0.[P1:X].[superclass: Generic<τ_0_0.[P2:A2], String, τ_0_0.[P2:B2]>] => τ_0_0.[P1:X]
35-
// CHECK: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
36-
// CHECK: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
37-
// CHECK: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
38-
// CHECK: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
39-
// CHECK: - τ_0_0.B2 => τ_0_0.[P1:B1]
40-
// CHECK: }
41-
// CHECK: Property map: {
42-
// CHECK-NEXT: [P1] => { conforms_to: [P1] }
43-
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, [P1:A1], [P1:B1]>] }
44-
// CHECK-NEXT: [P2] => { conforms_to: [P2] }
45-
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] }
46-
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
47-
// CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, String, τ_0_0.[P1:B1]>] }
48-
// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
49-
// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
50-
// CHECK-NEXT: }
37+
// CHECK-RULE-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
38+
// CHECK-RULE-NEXT: Rewrite system: {
39+
// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
40+
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<τ_0_0.[P2:A2], String, τ_0_0.[P2:B2]>] => τ_0_0.[P1:X]
41+
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
42+
// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
43+
// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
44+
// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
45+
// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1]
46+
// CHECK-RULE: }
47+
// CHECK-RULE: Property map: {
48+
// CHECK-RULE-NEXT: [P1] => { conforms_to: [P1] }
49+
// CHECK-RULE-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, [P1:A1], [P1:B1]>] }
50+
// CHECK-RULE-NEXT: [P2] => { conforms_to: [P2] }
51+
// CHECK-RULE-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] }
52+
// CHECK-RULE-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
53+
// CHECK-RULE-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Generic<Int, String, τ_0_0.[P1:B1]>] }
54+
// CHECK-RULE-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
55+
// CHECK-RULE-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
56+
// CHECK-RULE-NEXT: }
Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
// RUN: %target-typecheck-verify-swift -dump-requirement-machine 2>&1 | %FileCheck %s
1+
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s
2+
// RUN: %target-typecheck-verify-swift -dump-requirement-machine -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s --check-prefix=CHECK-RULE
23

34
// Note: The GSB fails this test, because it doesn't implement unification of
45
// superclass type constructor arguments.
56

7+
// FIXME: The Requirement Machine also fails to minimize the signature of
8+
// unifySuperclassTest(). rdar://90469643
9+
610
class Generic<T, U, V> {}
711

812
class Derived<TT, UU> : Generic<Int, TT, UU> {}
@@ -23,32 +27,34 @@ func sameType<T>(_: T.Type, _: T.Type) {}
2327

2428
func takesDerivedString<U>(_: Derived<String, U>.Type) {}
2529

30+
// CHECK-LABEL: .unifySuperclassTest@
31+
// CHECK-NEXT: Generic signature: <T where T : P1, T : P2>
2632
func unifySuperclassTest<T : P1 & P2>(_: T) {
2733
sameType(T.A1.self, String.self)
2834
sameType(T.A2.self, Int.self)
2935
sameType(T.B1.self, T.B2.self)
3036
takesDerivedString(T.X.self)
3137
}
3238

33-
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
34-
// CHECK-NEXT: Rewrite system: {
35-
// CHECK: - [P1:X].[layout: _NativeClass] => [P1:X]
36-
// CHECK: - [P2:X].[layout: _NativeClass] => [P2:X]
37-
// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
38-
// CHECK: - τ_0_0.[P1:X].[superclass: Generic<Int, τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
39-
// CHECK: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
40-
// CHECK: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
41-
// CHECK: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
42-
// CHECK: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
43-
// CHECK: - τ_0_0.B2 => τ_0_0.[P1:B1]
44-
// CHECK: }
45-
// CHECK: Property map: {
46-
// CHECK-NEXT: [P1] => { conforms_to: [P1] }
47-
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<[P1:A1], [P1:B1]>] }
48-
// CHECK-NEXT: [P2] => { conforms_to: [P2] }
49-
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] }
50-
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
51-
// CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] }
52-
// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
53-
// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
54-
// CHECK-NEXT: }
39+
// CHECK-RULE-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
40+
// CHECK-RULE-NEXT: Rewrite system: {
41+
// CHECK-RULE: - [P1:X].[layout: _NativeClass] => [P1:X]
42+
// CHECK-RULE: - [P2:X].[layout: _NativeClass] => [P2:X]
43+
// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
44+
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
45+
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
46+
// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
47+
// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
48+
// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
49+
// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1]
50+
// CHECK-RULE: }
51+
// CHECK-RULE: Property map: {
52+
// CHECK-RULE-NEXT: [P1] => { conforms_to: [P1] }
53+
// CHECK-RULE-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<[P1:A1], [P1:B1]>] }
54+
// CHECK-RULE-NEXT: [P2] => { conforms_to: [P2] }
55+
// CHECK-RULE-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] }
56+
// CHECK-RULE-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
57+
// CHECK-RULE-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] }
58+
// CHECK-RULE-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] }
59+
// CHECK-RULE-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] }
60+
// CHECK-RULE-NEXT: }

test/attr/accessibility.swift

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -207,31 +207,3 @@ extension GenericStruct where Param: InternalProto {
207207
public func foo() {} // expected-error {{cannot declare a public instance method in an extension with internal requirements}} {{3-9=internal}}
208208
}
209209

210-
211-
public class OuterClass {
212-
class InnerClass {}
213-
}
214-
215-
public protocol PublicProto2 {
216-
associatedtype T
217-
associatedtype U
218-
}
219-
220-
// FIXME: With the current design, the below should not diagnose.
221-
//
222-
// However, it does, because we look at the bound decl in the
223-
// TypeRepr first, and it happens to already be set.
224-
//
225-
// FIXME: Once we no longer do that, come up with another strategy
226-
// to make the above diagnose.
227-
228-
extension PublicProto2 where Self.T : OuterClass, Self.U == Self.T.InnerClass {
229-
public func cannotBePublic() {}
230-
// expected-error@-1 {{cannot declare a public instance method in an extension with internal requirements}}
231-
}
232-
233-
public extension OuterClass {
234-
open convenience init(x: ()) { self.init() }
235-
// expected-warning@-1 {{'open' modifier conflicts with extension's default access of 'public'}}
236-
// expected-error@-2 {{only classes and overridable class members can be declared 'open'; use 'public'}}
237-
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=off
2+
3+
public class OuterClass {
4+
class InnerClass {}
5+
}
6+
7+
public protocol PublicProto2 {
8+
associatedtype T
9+
associatedtype U
10+
}
11+
12+
// FIXME: With the current design, the below should not diagnose.
13+
//
14+
// However, it does, because we look at the bound decl in the
15+
// TypeRepr first, and it happens to already be set.
16+
//
17+
// FIXME: Once we no longer do that, come up with another strategy
18+
// to make the above diagnose.
19+
20+
// FIXME: Get this working with the Requirement Machine, or decide that it should
21+
// be unsupported: rdar://90469477
22+
23+
extension PublicProto2 where Self.T : OuterClass, Self.U == Self.T.InnerClass {
24+
public func cannotBePublic() {}
25+
// expected-error@-1 {{cannot declare a public instance method in an extension with internal requirements}}
26+
}
27+
28+
public extension OuterClass {
29+
open convenience init(x: ()) { self.init() }
30+
// expected-warning@-1 {{'open' modifier conflicts with extension's default access of 'public'}}
31+
// expected-error@-2 {{only classes and overridable class members can be declared 'open'; use 'public'}}
32+
}

0 commit comments

Comments
 (0)