Yet another question about concurrency

Hi everyone,

I’m trying to write a view extension similar to the task(id:priority:_) modifier that can accept several ids instead of just one.

In order to do that, I first wrote an EquatableTuple struct like this :

private struct EquatableTuple<each T: Equatable>: Equatable {
	let values: (repeat each T)
	
	init(_ values: repeat each T) {
		self.values = (repeat each values)
	}
	
	static func == (lhs: Self, rhs: Self) -> Bool {
		for isEqual in repeat each lhs.values == each rhs.values {
			guard isEqual else {
				return false
			}
		}
		return true
	}
}

Then the view extension itself :

public extension View {
	nonisolated func task<each T: Equatable>(id values: repeat each T, priority: TaskPriority = .userInitiated, @_inheritActorContext _ action: @Sendable @escaping () async -> Void) -> some View {
		return task(id: EquatableTuple(repeat each values), priority: priority, action)
	}

    // Redundant simple variant for 2 ids without the each syntax to illustrate the problem below 
	nonisolated func task<T1: Equatable, T2: Equatable>(id1 value1: T1, id2 value2: T2, priority: TaskPriority = .userInitiated, @_inheritActorContext _ action: @Sendable @escaping () async -> Void) -> some View {
		return task(id: EquatableTuple(value1, value2), priority: priority, action)
	}
}

Everything compiles fine but a problem arises when I try to use the view extension :

@MainActor
struct TestView: View {
	let a: Int
	let b: Int
	@State private var c = 0
	
	var body: some View {
		Text("Test")
			.task(id: a, b) {
				c = 1 // ERROR : Main actor-isolated property 'c' can not be mutated from a Sendable closure
			}
			.task(id1: a, id2: b) {
				c = 2 // Compiles fine, as expected
			}
	}
}

It looks like the first task extension with the repeat syntax isn't considering the @_inheritActorContext attribute of the closure and so the closure { c = 1 } in the view isn't inferred to be on the main actor.

Am I doing something wrong here ? Or is it a bug from the swift compiler ?

1 Like

Officially support for underscored functions and directives is non-existent so any bugs found in these functions or directives are solved on an as convenient/internally desired basis. Underscored functions are thereby only to be used with knowledge of the current implementation’s function and behavior.

This question is also likely a duplicate of: