From 0d6ac71084c0dacca7e6654b78e25d3c129021d8 Mon Sep 17 00:00:00 2001 From: aleksgapp Date: Wed, 12 Apr 2017 09:47:32 +0200 Subject: [PATCH] [SR-522][SR-629][SR-4161] Allow covariance in protocol conformance. --- lib/SILGen/SILGenPoly.cpp | 6 ++ lib/Sema/CSSimplify.cpp | 10 +++- lib/Sema/TypeCheckProtocol.cpp | 28 +++++---- test/ClangImporter/objc_parse.swift | 3 +- test/TypeCoercion/protocol_covariance.swift | 64 +++++++++++++++++++++ 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 test/TypeCoercion/protocol_covariance.swift diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index d83f4c88d7079..05a6b0285cd92 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -399,6 +399,12 @@ ManagedValue Transform::transform(ManagedValue v, inputOTK = OTK_None; } + if (inputOTK != OTK_None && outputOTK == OTK_None) { + SILValue result = SGF.B.createUncheckedBitCast(Loc, v.getValue(), + loweredResultTy); + return ManagedValue(result, v.getCleanup()); + } + // Optional-to-optional conversion. if (inputOTK != OTK_None && outputOTK != OTK_None) { // If the conversion is trivial, just cast. diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 484c7ab19a41b..c38c04cf54b7c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1038,9 +1038,13 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, increaseScore(ScoreKind::SK_FunctionConversion); - // Input types can be contravariant (or equal). - SolutionKind result = matchTypes(func2->getInput(), func1->getInput(), - subKind, subflags, + // Input types can be contravariant (or equal), + // but witness constranit might be covarian (or equal). + bool isWitnessConstraint = locator.last() && + locator.last()->getKind() == ConstraintLocator::Witness; + Type type1 = isWitnessConstraint ? func1->getInput() : func2->getInput(); + Type type2 = isWitnessConstraint ? func2->getInput() : func1->getInput(); + SolutionKind result = matchTypes(type1, type2, subKind, subflags, locator.withPathElement( ConstraintLocator::FunctionArgument)); if (result == SolutionKind::Error) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 112ce39637890..97b7480f158df 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1170,9 +1170,8 @@ matchWitness(TypeChecker &tc, // Initialized by the setup operation. Optional cs; - ConstraintLocator *locator = nullptr; ConstraintLocator *reqLocator = nullptr; - ConstraintLocator *witnessLocator = nullptr; + ConstraintLocator *locator = nullptr; Type witnessType, openWitnessType; Type openedFullWitnessType; Type reqType, openedFullReqType; @@ -1225,11 +1224,9 @@ matchWitness(TypeChecker &tc, // Open up the witness type. witnessType = witness->getInterfaceType(); - // FIXME: witness as a base locator? - locator = cs->getConstraintLocator(nullptr); - witnessLocator = cs->getConstraintLocator( - static_cast(nullptr), - LocatorPathElt(ConstraintLocator::Witness, witness)); + locator = cs->getConstraintLocator( + static_cast(nullptr), + LocatorPathElt(ConstraintLocator::Witness, witness)); OpenedTypeMap witnessReplacements; if (witness->getDeclContext()->isTypeContext()) { std::tie(openedFullWitnessType, openWitnessType) @@ -1237,7 +1234,7 @@ matchWitness(TypeChecker &tc, /*isTypeReference=*/false, /*isDynamicResult=*/false, FunctionRefKind::DoubleApply, - witnessLocator, + locator, /*base=*/nullptr, &witnessReplacements); } else { @@ -1246,7 +1243,7 @@ matchWitness(TypeChecker &tc, /*isTypeReference=*/false, /*isSpecialized=*/false, FunctionRefKind::DoubleApply, - witnessLocator, + locator, /*base=*/nullptr); } openWitnessType = openWitnessType->getRValueType(); @@ -1255,9 +1252,16 @@ matchWitness(TypeChecker &tc, }; // Match a type in the requirement to a type in the witness. - auto matchTypes = [&](Type reqType, Type witnessType) + auto matchTypes = [&](Type reqType, Type witnessType) -> Optional { - cs->addConstraint(ConstraintKind::Equal, reqType, witnessType, locator); + ConstraintKind kind = ConstraintKind::Conversion; + if (auto func = dyn_cast(req)) { + auto name = req->getFullName().getBaseName(); + if (func->isOperator() && name.str() == "==") { + kind = ConstraintKind::Equal; + } + } + cs->addConstraint(kind, witnessType, reqType, locator); // FIXME: Check whether this has already failed. return None; }; @@ -1286,7 +1290,7 @@ matchWitness(TypeChecker &tc, // Compute the set of substitutions we'll need for the witness. solution->computeSubstitutions( witness->getInnermostDeclContext()->getGenericSignatureOfContext(), - witnessLocator, + locator, result.WitnessSubstitutions); return result; diff --git a/test/ClangImporter/objc_parse.swift b/test/ClangImporter/objc_parse.swift index d4bfd8b4b15c7..8b42adc44858c 100644 --- a/test/ClangImporter/objc_parse.swift +++ b/test/ClangImporter/objc_parse.swift @@ -486,8 +486,7 @@ func testWeakVariable() { } class IncompleteProtocolAdopter : Incomplete, IncompleteOptional { // expected-error {{type 'IncompleteProtocolAdopter' cannot conform to protocol 'Incomplete' because it has requirements that cannot be satisfied}} - // expected-error@-1{{type 'IncompleteProtocolAdopter' does not conform to protocol 'Incomplete'}} - @objc func getObject() -> AnyObject { return self } // expected-note{{candidate has non-matching type '() -> AnyObject'}} + @objc func getObject() -> AnyObject { return self } } func testNullarySelectorPieces(_ obj: AnyObject) { diff --git a/test/TypeCoercion/protocol_covariance.swift b/test/TypeCoercion/protocol_covariance.swift new file mode 100644 index 0000000000000..614e4a5632044 --- /dev/null +++ b/test/TypeCoercion/protocol_covariance.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +class A {} +class B: A {} + +protocol P { + func f1(o: Int?) + func f2() -> Int? +} + +protocol P1 { + var a: A { get set } + func f() -> A +} + +struct S : P1 { + func f() -> B { + return B() + } + var a = B() +} + +extension S: P { + func f1(o: Int) { + print(o) + } + func f2() -> Int { + return 1 + } +} + +class C: P1 { + func f() -> G { + return B() as! G + } + var a: G = B() as! G +} + + +protocol P2 { + func f(_:(Int?)->()) +} +struct X : P2 { + func f(_:(Int)->()) {} +} + +protocol Q { + var i: Int? { get } +} +struct Y : Q { + var i: Int = 0 +} + +protocol A1 {} +protocol B2 {} + +protocol HasA1 { + var a: A1 { get } +} + +struct Baz: HasA1 { + var a: A1 & B2 +} +