I don't think we'd introduce undefined behavior. We'd have dynamic casts that fail for hard-to-explain reasons, at worst.
-Joe
···
On Mar 2, 2016, at 10:10 PM, Douglas Gregor <dgregor@apple.com> wrote:
On Mar 2, 2016, at 9:42 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
On Mar 2, 2016, at 9:32 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:
On Mar 2, 2016, at 9:12 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
On Mar 2, 2016, at 8:26 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:
On Mar 2, 2016, at 5:38 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
On Mar 2, 2016, at 5:22 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Private conformances
Right now, a protocol conformance can be no less visible than the minimum of the conforming type’s access and the protocol’s access. Therefore, a public type conforming to a public protocol must provide the conformance publicly. One could imagine removing that restriction, so that one could introduce a private conformance:
public protocol P { }
public struct X { }
extension X : internal P { … } // X conforms to P, but only within this module
The main problem with private conformances is the interaction with dynamic casting. If I have this code:
func foo(value: Any) {
if let x = value as? P { print(“P”) }
}
foo(X())
Under what circumstances should it print “P”? If foo() is defined within the same module as the conformance of X to P? If the call is defined within the same module as the conformance of X to P? Never? Either of the first two answers requires significant complications in the dynamic casting infrastructure to take into account the module in which a particular dynamic cast occurred (the first option) or where an existential was formed (the second option), while the third answer breaks the link between the static and dynamic type systems—none of which is an acceptable result.
You don't need private conformances to introduce these coherence problems with dynamic casting. You only need two modules that independently extend a common type to conform to a common protocol. As Jordan discussed in his resilience manifesto, a publicly-subclassable base class that adopts a new protocol has the potential to create a conflicting conformance with external subclasses that may have already adopted that protocol.
Right, multiple conformances do happen in our current model. Personally, I think that the occurrence of multiple conformances should effectively be an error at runtime unless the conformances are effectively identical (same type witnesses with the same conformances may be a reasonable approximation), and even then it’s worthy of a diagnostic as early as we can produce one, because the amount of infrastructure one needs to handle multiple conformances is significant.
If it's a runtime error, that's a huge resilience liability, since any library adding a conformance would potentially be causing its users to start crashing at load time.
My hope is that we could limit the failure mode to the case where the conflicting conformances are different in a way that affects soundness, e.g., different type witnesses for the same associated type.
I don't think we can reliably tell. As currently implemented, we generate a fresh set of witness thunks for every conformance, so the function pointers in the two modules' conformances would look different to the runtime.
I was thinking we only “have” to compare the type witnesses and any conformances they carry with them, because that’s what we need to ensure type soundness.
I certainly agree that we’re creating both a resilience liability and a liability when composing libraries (the author of library E below did nothing wrong, but observes weird behavior), but the alternatives seem so much worse.
The only place I can see where we rely on coherence is in the runtime, as you've mentioned. We're pretty good about statically maintaining conformance identity through the stack, so we ought to be able to catch static conformance mismatches in types and maybe even give reasonable error messages. It wouldn't be hard for the runtime to notice when it has a key collision in its conformance table and log a warning either—that would be consistent with what the ObjC runtime does with class or category collisions between images.
Right. My concern is that, in the case where we’ve lost soundness, we need to trap to avoid introducing undefined behavior. Perhaps I’m worrying overmuch.
public class GenericReturnTypes {
interface Foo {
static <Output extends Number> Output foo() {
return (Output)(new Integer(0));
}
}
public static void main(String args) {
System.out.println(Foo.foo());
}
}
Compiles without error or warning, runs, and prints 0.
-- Howard.
···
On 11 March 2016 at 02:18, Thorsten Seitz <tseitz42@icloud.com> wrote:
Am 10.03.2016 um 05:35 schrieb Howard Lovatt via swift-evolution < > swift-evolution@swift.org>:
I am sure you know the answer for Swift; but in other languages it
doesn't, e.g. Java.
With Java semantics:
func foo<Input, Output>(input: Input) -> Output
Would be the same as:
func foo(input: Any) -> Any
Because the only constraint on both Input and Output is Any. Since the
Java compiler erases generics, the two would literally be the same to the
JVM!
To the JVM at runtime, but not to the typechecker at compile time. There
it works just as in Swift.
-Thorsten
Since Swift doesn't allow overloading on return type and therefore as it
stands `func foo<Input, Output>(input: Input) -> Output` cannot
be implemented, it would be nice if the compiler flagged this as an error
and suggested that you meant `func foo(input: Any) -> Any`.
-- Howard.
On 10 March 2016 at 13:07, Joe Groff <jgroff@apple.com> wrote:
> On Mar 9, 2016, at 6:02 PM, Howard Lovatt <howard.lovatt@gmail.com> >> wrote:
>
> Wow, I would never have guessed that syntax :)
>
> It makes no sense to me to interpret a generic constraint as meaning
all instead of any. How could anything either accept or return all possible
implementations of something simultaneously, surely it only ever accepts or
returns one of all the possible implementations at a time.
A type variable in angle brackets always means "all". It's like a
function parameter, but at type level—it's in the caller's hands what it
gets bound to. You couldn't write a function `func foo<Input,
>(Input) -> Output` unless that function was able to generate a value
of every possible type a caller might pass in for Output, just like you
couldn't write e.g. 'absolute value' as taking its result as a second
parameter.
>
> If the interpretation for output is that at time 1 it returns one of
all the possible implementations at at time 2 returns another - then that
is what I want. However I would describe that as returning one of the
possible implementations, not all.
>
> But no doubt you are correct, since you probably wrote that bit of the
compiler :(
>
> More practical points
>
> 1. My compiler, 7.3 beta (7D152p), rejects the syntax, it doesn't like
where inside Any<> saying it expects > to complete generic argument list.
When will this be available, so that I can try it out?
> 2. Will the declarations inside protocols also change to the Any<...>
form or will all the generics remain following the function name rather in
a returned Any<...>? Currently -> Any<...> doesn't work in protocols for me.
> 3. In the construct Any<Protocol where Type == Type>, i.e. same type
name used in protocol and enclosing struct/class/extension, does the left
Type refer to the protocol or the enclosing struct/class/extension?
> 4. Is there any documentation of all of this?
Sorry, this is all possible future syntax and features. It's not
implemented today. You'd need to write your own equivalent of the "Any"
wrapper by hand right now.
My point was that `static <Output extends Number> Output foo()` in Java is analogous to `func foo<Output>() -> Output` in Swift. In both languages, the Output type variable is chosen by the caller, so can be all possible types matching those constraints.
-Joe
···
On Mar 10, 2016, at 1:59 PM, Howard Lovatt <howard.lovatt@gmail.com> wrote: