Skip to content

Commit 2c1e329

Browse files
committed
[docs] LibraryEvolution: Add some known problematic scenarios.
These are pretty sketchy, but they do cause issues.
1 parent 0db4f95 commit 2c1e329

File tree

1 file changed

+102
-6
lines changed

1 file changed

+102
-6
lines changed

docs/LibraryEvolution.rst

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,13 +1087,11 @@ following changes are permitted:
10871087
- Removing any non-public, non-versioned member.
10881088
- Changing the body of any methods, initializers, or accessors.
10891089

1090-
.. admonition:: TODO
1091-
1092-
How does "adding any new member" square with the restrictions on adding
1093-
initializers to classes? Are they inherently convenience initializers?
1090+
.. note::
10941091

1095-
What about everything else? New members in protocols can result in new
1096-
overridable members in classes. How does that work?
1092+
Although it is not related to evolution, it is worth noting that members of
1093+
protocol extensions that do *not* satisfy protocol requirements are not
1094+
overrideable, even when the conforming type is a class.
10971095

10981096

10991097
Operators
@@ -1454,6 +1452,104 @@ Because this tool would require a fair amount of additional work, it is not
14541452
part of this initial model. It is something we may decide to add in the future.
14551453

14561454

1455+
Open Issues
1456+
===========
1457+
1458+
There are still a number of known issues with the model described in this
1459+
document. We should endeavour to account for each of them, and if we can't come
1460+
up with a satisfactory implementation we should at least make sure that they
1461+
will not turn into pitfalls for library or client developers.
1462+
1463+
1464+
Subclass and base both conform to protocol
1465+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1466+
1467+
::
1468+
1469+
// Library, version 1
1470+
class Base {}
1471+
protocol P {}
1472+
1473+
::
1474+
1475+
// Client, version 1
1476+
class Subclass : Base, P {}
1477+
1478+
::
1479+
1480+
// Library, version 2
1481+
@available(2.0)
1482+
extension Base : P {}
1483+
1484+
Now ``Subclass`` conforms to ``P`` two different ways, which may be
1485+
incompatible (especially if ``P`` has associated types or requirements
1486+
involving ``Self``).
1487+
1488+
Additionally, the client can't even remove ``Subclass``'s conformance to ``P``,
1489+
because it may itself be a library with other code depending on it. We could
1490+
fix that with an annotation to explicitly inherent the conformance of ``P``
1491+
from the base class, but even that may not be possible if there are
1492+
incompatible associated types involved (because changing a member typealias is
1493+
not a safe change).
1494+
1495+
One solution is to disallow adding a conformance for an existing protocol to a
1496+
publicly-subclassable class.
1497+
1498+
1499+
Recompiling changes a protocol's implementation
1500+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1501+
1502+
::
1503+
1504+
// Library, version 1
1505+
protocol P {}
1506+
protocol Q {}
1507+
func use<T: P>(value: T) {}
1508+
1509+
::
1510+
1511+
// Client, version 1
1512+
struct S : P, Q {}
1513+
use(S())
1514+
1515+
::
1516+
1517+
// Library, version 2
1518+
protocol P {
1519+
@available(2.0)
1520+
func foo() { print("default") }
1521+
}
1522+
1523+
extension P where Self: Q {
1524+
@available(2.0)
1525+
func foo() { print("constrained") }
1526+
}
1527+
1528+
func use<T: P>(value: T) { value.foo() }
1529+
1530+
Before the client is recompiled, the implementation of ``foo()`` used for ``S``
1531+
instances can only be the default implementation, i.e. the one that prints
1532+
"default". However, recompiling the client will result in the constrained
1533+
implementation being considered a "better" match for the protocol requirement,
1534+
thus changing the behavior of the program.
1535+
1536+
This should never change the *meaning* of a program, since the default
1537+
implementation for a newly-added requirement should always be *correct.*
1538+
However, it may have significantly different performance characteristics or
1539+
side effects that would make the difference in behavior a surprise.
1540+
1541+
This is similar to adding a new overload to an existing set of functions, which
1542+
can also change the meaning of client code just by recompiling. However, the
1543+
difference here is that the before-recompilation behavior was never requested
1544+
or acknowledged by the client; it's just the best the library can do.
1545+
1546+
A possible solution here is to require the client to acknowledge the added
1547+
requirement in some way when it is recompiled.
1548+
1549+
(We do not want to perform overload resolution at run time to find the best
1550+
possible default implementation for a given type.)
1551+
1552+
14571553
Summary
14581554
=======
14591555

0 commit comments

Comments
 (0)