Files
swift-mirror/test/Constraints/protocols.swift
Slava Pestov 6812b6926b Sema: Cleanups and minor fixes for protocol 'Self' types
We had four duplicated implementations of checking how a protocol
requirement uses 'Self', all slightly wrong or incomplete:

- When deciding if the protocol type can be used as an existential.
  This one would just ignore 'Self' in the return type of a method
  completely, which was incorrect for cases where 'Self' is
  contravariant but part of the return value, for example:

  func foo() -> (Self -> ())

- When deciding if a member access can be performed on an existential
  value. This is distinct from the former, because the member may
  have been defined in a protocol extension, in which case it cannot
  be used even if the protocol type can be used as an existential.
  Unfortunately, this implementation was overly conservative, and
  would reject uses of 'Self' where Sema could in fact erase the
  existential type, for example:

  func foo() -> Self??
  func foo() -> Self.Type
  func foo() -> (Self, Self)

  This function handled function return types correctly, effectively
  plugging the leak in the previous code. It did lead to inconsistent
  behavior with protocols that had contravariant Self in requirements
  though; sometimes we would diagnose uses of the existential type,
  other times we would only complain about specific members.

- When deciding if a method in a non-final class can model a protocol
  requirement. This one was the most elaborate one, but here
  contravariance and uses of associated types are actually okay, so
  it was written to pick up covariant 'Self' only. However, it also
  did not handle metatypes and tuples.

- When opening the type of member of an existential, we would check
  if the return value was 'Self' or an optional of 'Self', but again
  this check was too conservative, so after the previous three were
  fixed, we could reference members on existentials that did not
  have a correct opened type.

Now, these have been combined into one check. To fix some crashes,
Sema's implementation of erasing existentials now relies on
coerceToType() instead of hand-rolling a few coercions of its own,
and wrapping the rest in CovariantFunctionConversionExpr, which
didn't make much sense if the result was not a function type.

SILGen still does not support function type conversions where an
existential return value is being erased; these would silently
miscompile before, but crash with an assertion now, because they
are correctly modeled as a FunctionConversionExpr, and not
CovariantFunctionConversionExpr.
2016-02-28 23:52:35 -08:00

119 lines
3.5 KiB
Swift

// RUN: %target-parse-verify-swift
protocol Fooable { func foo() }
protocol Barable { func bar() }
extension Int : Fooable, Barable {
func foo() {}
func bar() {}
}
extension Float32 : Barable {
func bar() {}
}
func f0(_: Barable) {}
func f1(x: protocol<Fooable, Barable>) {}
func f2(_: Float) {}
let nilFunc: Optional<(Barable) -> ()> = nil
func g(_: (protocol<Barable, Fooable>) -> ()) {}
protocol Classable : AnyObject {}
class SomeArbitraryClass {}
func fc0(_: Classable) {}
func fc1(_: protocol<Fooable, Classable>) {}
func fc2(_: AnyObject) {}
func fc3(_: SomeArbitraryClass) {}
func gc(_: (protocol<Classable, Fooable>) -> ()) {}
var i : Int
var f : Float
var b : Barable
//===----------------------------------------------------------------------===//
// Conversion to and among existential types
//===----------------------------------------------------------------------===//
f0(i)
f0(f)
f0(b)
f1(i)
f1(f) // expected-error{{argument type 'Float' does not conform to expected type 'protocol<Barable, Fooable>'}}
f1(b) // expected-error{{argument type 'Barable' does not conform to expected type 'protocol<Barable, Fooable>'}}
//===----------------------------------------------------------------------===//
// Subtyping
//===----------------------------------------------------------------------===//
g(f0) // okay (subtype)
g(f1) // okay (exact match)
g(f2) // expected-error{{cannot convert value of type '(Float) -> ()' to expected argument type '(protocol<Barable, Fooable>) -> ()'}}
// FIXME: Workaround for ?? not playing nice with function types.
infix operator ??* {}
func ??*<T>(lhs: T?, rhs: T) -> T { return lhs ?? rhs }
g(nilFunc ??* f0)
gc(fc0) // okay
gc(fc1) // okay
gc(fc2) // okay
gc(fc3) // expected-error{{cannot convert value of type '(SomeArbitraryClass) -> ()' to expected argument type '(protocol<Classable, Fooable>) -> ()'}}
// rdar://problem/19600325
func getAnyObject() -> AnyObject? {
return SomeArbitraryClass()
}
func castToClass(object: Any) -> SomeArbitraryClass? {
return object as? SomeArbitraryClass
}
_ = getAnyObject().map(castToClass)
_ = { (_: Any) -> Void in
return
} as ((Int) -> Void)
let _: (Int) -> Void = {
(_: Any) -> Void in
return
}
let _: () -> Any = {
() -> Int in
return 0
}
let _: () -> Int = {
() -> String in // expected-error {{declared closure result 'String' is incompatible with contextual type 'Int'}}
return ""
}
//===----------------------------------------------------------------------===//
// Dynamic self
//===----------------------------------------------------------------------===//
protocol Clonable {
func maybeClone() -> Self?
func doubleMaybeClone() -> Self??
func subdivideClone() -> (Self, Self)
func metatypeOfClone() -> Self.Type
func badClonerFn() -> (Self -> Self)
func veryBadClonerFn() -> ((inout Self) -> ())
func goodClonerFn() -> (() -> Self)
}
func testClonable(v : Clonable) { // expected-error {{protocol 'Clonable' can only be used as a generic constraint because it has Self or associated type requirements}}
let v2 = v.maybeClone()
let v3 = v.doubleMaybeClone()
let v4 = v.subdivideClone()
let v5 = v.metatypeOfClone()
let v6 = v.goodClonerFn()
let v7 = v.badClonerFn() // expected-error {{member 'badClonerFn' cannot be used on value of protocol type 'Clonable'; use a generic constraint instead}}
let v8 = v.veryBadClonerFn() // expected-error {{member 'veryBadClonerFn' cannot be used on value of protocol type 'Clonable'; use a generic constraint instead}}
}