Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ public class ConcreteProtocolAB: ProtocolA, ProtocolB {
MySwiftClass(x: 10, y: 50)
}
}

/// Returns a **reference-type** (class) `ProtocolA` conformer boxed as `any
/// ProtocolA`, to exercise existential-box setter write-back against a
/// class conformer.
public func makeProtocolAClass(constantA: Int64, constantB: Int64) -> any ProtocolA {
ConcreteProtocolAB(constantA: constantA, constantB: constantB)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,27 @@ public protocol ProtocolA {
public func takeProtocol(_ proto1: any ProtocolA, _ proto2: some ProtocolA) -> Int64 {
proto1.constantA + proto2.constantA
}

/// A struct conformer to `ProtocolA`, used to prove that
/// setter dispatch through a returned existential box
public struct ConcreteProtocolAStruct: ProtocolA {
public let constantA: Int64
public var mutable: Int64 = 0

public init(constantA: Int64) {
self.constantA = constantA
}

public func name() -> String {
"ConcreteProtocolAStruct"
}

public func makeClass() -> MySwiftClass {
MySwiftClass(x: constantA, y: mutable)
}
}

/// Returns a value-type `ProtocolA` conformer boxed as `any ProtocolA`.
public func makeProtocolA(constantA: Int64) -> any ProtocolA {
ConcreteProtocolAStruct(constantA: constantA)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ public struct ConcreteProtocolC: ProtocolC {
constantC = c
}
}

public func makeProtocolC(b: Int64, c: Int64) -> any ProtocolC {
ConcreteProtocolC(b: b, c: c)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ public func useProtocolWithStatic(_ value: any ProtocolWithStatic) -> Int {
return meta.myFunc()
}

public struct ConcreteProtocolWithStatic: ProtocolWithStatic {
public init() {}

public static func myFunc() -> Int {
42
}
}

public func makeProtocolWithStatic() -> any ProtocolWithStatic {
ConcreteProtocolWithStatic()
}

public protocol ProtocolWithStaticProperty {
static var value: Int { get }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

public protocol Greeter {
func greeting() -> String
func repeated(count: Int64) -> String
}

public struct EnglishGreeter: Greeter {
public let name: String

public init(name: String) {
self.name = name
}

public func greeting() -> String {
"Hello, \(name)!"
}

public func repeated(count: Int64) -> String {
Array(repeating: greeting(), count: Int(count)).joined(separator: " ")
}
}

public struct DanishGreeter: Greeter {
public let name: String

public init(name: String) {
self.name = name
}

public func greeting() -> String {
"Hej, \(name)!"
}

public func repeated(count: Int64) -> String {
Array(repeating: greeting(), count: Int(count)).joined(separator: " ")
}
}

public func makeEnglishGreeter(name: String) -> any Greeter {
EnglishGreeter(name: name)
}

public func makeDanishGreeter(name: String) -> any Greeter {
DanishGreeter(name: name)
}

public func makeOpaqueGreeter(name: String) -> some Greeter {
EnglishGreeter(name: name)
}

public func describeGreeter(_ greeter: any Greeter) -> String {
greeter.greeting()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

public struct GreeterError: Error {}

public protocol RichGreeter {
func nickname() -> String?
func aliases() -> [String]
func decorate(_ object: MySwiftClass) -> MySwiftClass
func greetOrThrow(shouldThrow: Bool) throws -> String
func recordGreeting()
func count() -> Int64
}

public final class RichGreeterImpl: RichGreeter {
private var greetings: Int64 = 0
private let base: String

public init(base: String) {
self.base = base
}

public func nickname() -> String? {
base.isEmpty ? nil : "Mr. \(base)"
}

public func aliases() -> [String] {
[base, base + base]
}

public func decorate(_ object: MySwiftClass) -> MySwiftClass {
MySwiftClass(x: object.x + 1, y: object.y + 1)
}

public func greetOrThrow(shouldThrow: Bool) throws -> String {
if shouldThrow {
throw GreeterError()
}
return "Hi \(base)"
}

public func recordGreeting() {
greetings += 1
}

public func count() -> Int64 {
greetings
}
}

public func makeRichGreeter(base: String) -> any RichGreeter {
RichGreeterImpl(base: base)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

package com.example.swift;

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.JNISwiftInstance;
import org.swift.swiftkit.core.SwiftArena;

import static org.junit.jupiter.api.Assertions.*;

public class ReturnInheritedProtocolTest {
@Test
void returnedRefinedProtocolExposesOwnAndInheritedRequirements() {
try (var arena = SwiftArena.ofConfined()) {
ProtocolC protoC = MySwiftLibrary.makeProtocolC(7, 11, arena);

// Own requirement, declared directly on ProtocolC.
assertEquals(11, protoC.getConstantC());

// Inherited requirement, declared on ProtocolB, which ProtocolC refines.
assertEquals(7, protoC.getConstantB());
}
}

@Test
void returnedRefinedProtocolIsAlsoUsableAsParentProtocol() {
try (var arena = SwiftArena.ofConfined()) {
ProtocolC protoC = MySwiftLibrary.makeProtocolC(7, 11, arena);
ProtocolB protoB = protoC;
assertEquals(7, protoB.getConstantB());
assertEquals(7, MySwiftLibrary.takeProtocolB(protoB));
}
}

@Test
void returnedRefinedProtocolIsABackingSwiftInstance() {
try (var arena = SwiftArena.ofConfined()) {
ProtocolC protoC = MySwiftLibrary.makeProtocolC(7, 11, arena);
assertInstanceOf(JNISwiftInstance.class, protoC);
}
}

@Test
void returnedProtocolBoxSetterWritesBackToValueTypeStorage() {
try (var arena = SwiftArena.ofConfined()) {
// ConcreteProtocolAStruct is a *value type* (struct) conformer to
// ProtocolA. Opening the existential returned by makeProtocolA
// yields a copy of the struct, so if setMutable merely mutated the
// opened copy without writing it back into the box's storage, the
// new value would be silently lost and getMutable() would still
// read 0.
ProtocolA proto = MySwiftLibrary.makeProtocolA(10, arena);
assertEquals(10, proto.getConstantA());
assertEquals(0, proto.getMutable());

proto.setMutable(42);
assertEquals(42, proto.getMutable());
}
}

@Test
void returnedProtocolBoxSetterWritesBackToReferenceTypeStorage() {
try (var arena = SwiftArena.ofConfined()) {
// ConcreteProtocolAB is a *reference type* (class) conformer to
// ProtocolA, exercising the same write-back path against a class
// conformer boxed as any ProtocolA.
ProtocolA proto = MySwiftLibrary.makeProtocolAClass(10, 5, arena);
assertEquals(10, proto.getConstantA());
assertEquals(0, proto.getMutable());

proto.setMutable(7);
assertEquals(7, proto.getMutable());
}
}

@Test
void returnedProtocolWithUnsupportedStaticRequirementIsStillUsable() {
try (var arena = SwiftArena.ofConfined()) {
// ProtocolWithStatic declares a static func and an init(), neither of which
// are supported requirements for existential boxing, so ProtocolWithStaticBox
// simply omits them. The box/interface should still come back usable as a
// JNISwiftInstance-backed reference.
ProtocolWithStatic protoWithStatic = MySwiftLibrary.makeProtocolWithStatic(arena);
assertInstanceOf(JNISwiftInstance.class, protoWithStatic);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

package com.example.swift;

import org.junit.jupiter.api.Test;
import org.swift.swiftkit.core.SwiftArena;

import static org.junit.jupiter.api.Assertions.*;

public class ReturnProtocolDispatchTest {
@Test
void boxMethodReturningJextractedClass() {
try (var arena = SwiftArena.ofConfined()) {
ProtocolA proto = MySwiftLibrary.makeProtocolA(10, arena);
assertEquals(10, proto.makeClass(arena).getX());
}
}

@Test
void boxSetterWriteBackObservableThroughSecondRequirement() {
try (var arena = SwiftArena.ofConfined()) {
// ConcreteProtocolAStruct.makeClass() returns
// MySwiftClass(x: constantA, y: mutable), so observing the
// write-back of setMutable through makeClass()'s y (rather than
// through getMutable() itself) proves the setter thunk wrote the
// new value into the box's actual backing storage: makeClass()
// is a *different* requirement that independently reads that
// storage.
ProtocolA proto = MySwiftLibrary.makeProtocolA(10, arena);
proto.setMutable(42);
assertEquals(42, proto.makeClass(arena).getY());
}
}

@Test
void returnedBoxesRoundTripIntoProtocolParameter() {
try (var arena = SwiftArena.ofConfined()) {
assertEquals(
30,
MySwiftLibrary.takeProtocol(
MySwiftLibrary.makeProtocolA(10, arena),
MySwiftLibrary.makeProtocolA(20, arena)
)
);
}
}

@Test
void returnedBoxesRoundTripIntoGenericProtocolParameters() {
try (var arena = SwiftArena.ofConfined()) {
// A ProtocolA box and a ProtocolC box (whose protocol refines
// ProtocolB) fed into takeGenericProtocol<First: ProtocolA,
// Second: ProtocolB>; the ProtocolC box must satisfy the
// ProtocolB generic bound.
assertEquals(
17,
MySwiftLibrary.takeGenericProtocol(
MySwiftLibrary.makeProtocolA(10, arena),
MySwiftLibrary.makeProtocolC(7, 11, arena)
)
);
}
}
}
Loading
Loading