-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix source compatibility problems with conforming to multiple Collection axes at once. #7136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix source compatibility problems with conforming to multiple Collection axes at once. #7136
Conversation
@swift-ci Please test |
@airspeedswift and @dabrahams, how do the standard library changes here look? @DougGregor or @rudkx, do the type checker changes look alright for 3.1? |
|
||
// Make sure that solution is better than any of the other solutions | ||
// Make sure that solution is better than any of the other solutionsa |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
solutionsa
Build failed |
I suspect PR #4825 might be what surfaced this problem. This is the first time that change has made it off master into a full release. |
public subscript(bounds: Range<Index>) -> MutableRandomAccessSlice<Self> { | ||
get { | ||
_failEarlyRangeCheck(bounds, bounds: startIndex..<endIndex) | ||
return .init(base: self, bounds: bounds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
never thought about calling init
like this, very cute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying hard not to decide it's too cute by half. Maybe it's just a new idiom we should be using everywhere, but at first glance it appears to harm readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern was gyb-ability. I can expand out the name if you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it, especially for short functions. Why repeat the name. And makes the fact that you're creating the thing you're returning clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@airspeedswift The reason why repeat it is that despite years of Swift I still think of .xxx
as looking up xxx
in the current scope instead of the type context. It feels as though it's chaining to Self.init
. Of course I'm projecting, but I have a hard time seeing how other people don't make the same mistake. Finally, I don't see how it clarifies anything useful: CapitalizedName(...)
is always creating something.
@jckarter how does "gyb-ability" come into play?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only that I judged init(...)
to be clearer than repeating the complicated ${foo}${bar}${bas}Slice
expansion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess gyb makes the .init
thing more appealing since you have to return an ugly gybbed typename like RangeReplaceable${capability}Slice
(not in this example, but in some others).
Re the working of .xxx
... that seems like just something to get over, since it's pervasive – this is just one more example. Sure, CapitalizedName(...)
means creation but I think the bare .init
does make it clearer what it's doing "at a glance" once you're used to it, to me at least, and also eliminates any chance of there being an additional subtype conversion you could be overlooking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes LGTM. There are probably additional tests needed, like to test that slices have the expected properties, but we can merge without then add them later, will raise a Jira for that. We’ll need that kind of test for testing the conditional conformance refactoring too.
where C.Iterator.Element == Int | ||
{} | ||
% end | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice thorough testing, Joe!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modulo a decision about the .init(
...)
cuteness, it looks great.
on Sun Jan 29 2017, Ben Cohen <notifications-AT-i.8713187.xyz> wrote:
Changes LGTM. There are probably additional tests needed, like to test
that slices have the expected properties
There ought to be prepackaged checks for that stuff in StdlibUnittest
…--
-Dave
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fantastic, @jckarter!
@@ -2976,7 +2977,54 @@ InferredAssociatedTypesByWitnesses | |||
ConformanceChecker::inferTypeWitnessesViaValueWitnesses(ValueDecl *req) { | |||
InferredAssociatedTypesByWitnesses result; | |||
|
|||
auto isExtensionUsableForInference = [&](ExtensionDecl *extension) -> bool { | |||
// Assume concrete extensions we found witnesses in are always viable. | |||
if (!extension->getExtendedType()->isAnyExistentialType()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assumption isn't quite true: constrained extensions of concrete types won't be able to provide suitable witnesses, so that return true;
could more accurately be:
return !extension->isConstrainedExtension();
for now. When conditional conformances come in, we'll need an "isAtLeastAsSpecializedAs" kind of check.
// | ||
// TODO: This seems like it shouldn't happen, but without this check, we | ||
// crash building the standard library. | ||
if (!extension->getGenericSignature()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's it's recursion breaker.
for (auto witness : lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) { | ||
// If the potential witness came from a protocol extension, and our `Self` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can drop the "protocol" part of this comment, if you take my suggestion above regarding constrained extensions of concrete types.
@@ -3780,6 +3850,154 @@ void ConformanceChecker::resolveTypeWitnesses() { | |||
// If we (still) have more than one solution, find the one with | |||
// more-specialized witnesses. | |||
if (solutions.size() > 1) { | |||
auto compareDeclsForInference = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you pull this out into a static function? It's pretty large to be inline.
…inference. The associated type inference is a bit unusual, since we want to be able to infer associated types from protocol extension implementations that themselves depend on the types being inferred a certain way, e.g.: ``` protocol Runcible { associatedtype Spoon; func runce(_: Spoon) } extension Runcible where Spoon == Hat { func runce(_ hat: Hat) {} } ``` For this reason, we would collect potential witnesses from all possible extensions of the type without immediately checking the constraints on each extension. This could lead to an inconsistency where, when ranking potential witnesses, associated type inference would end up picking the "best" candidate as something that can't even be used by the type we're inferring the witness for, leading to incomprehensible behavior. The ranking criterion itself is also flawed, because `TC.compareDeclarations` is normally comparing candidates for the same well-typed function invocation, but during associated type inference we haven't even established the type system we're comparing candidates within. Witness candidates from extensions with mutually exclusive associated type constraints but otherwise more specific constraints would be unnecessarily dismissed as ambiguous because neither candidate's generic signature is a match for the other's. Both problems can be mitigated by looking at only the constraints for protocol extensions that specifically constrain the `Self` type, since those conformances are always explicit in the source code and therefore independent of associated type inference. We can pre-filter witness candidates by rejecting candidates for which the conforming type doesn't match the extension's Self constraints. We can score multiple witness candidates by looking only at the relative constraints they put on `Self`, considering a witness from a context that more specifically constrains Self to be a better candidate; we should ignore the associated type constraints for this scoring because we don't even know if they apply given the solution we'll ultimately pick. There's probably a more systemic way to go about this, but targeted fixes for both of these problems, along with a following patch to the standard library, restores source compatibility for Collection conformers, fixing rdar://problem/30228957.
…ex>) for combinations of [Mutable][RangeReplaceable][Bidirectional|RandomAccess]Collection. These were overlooked, and somehow code that attempted to make a minimal collection conform to RangeReplaceableCollection and RandomAccessCollection managed to compile successfully in Swift 3.0, but in Swift 3.1…*something* changed to reject a type that conforms to both due to the lack of a suitable default slicing subscript implementation in the stdlib that provided all the requirements. Fill in these missing implementations, fixing rdar://problem/30228957.
7774d9e
to
34ec424
Compare
@airspeedswift @dabrahams I went ahead and expanded out the uses of @DougGregor I also made the type checker changes you requested. How does this look? |
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
Build failed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
@swift-ci Please test Linux |
Awesome, thanks! |
% for rangeReplaceable in ['', 'RangeReplaceable']: | ||
% for capability in ['', 'Bidirectional', 'RandomAccess']: | ||
|
||
struct ${mutable}${rangeReplaceable}${capability}Butt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put some 👖on that identifier!
Sema: Improve handling of potential witnesses during associated type inference.
The associated type inference is a bit unusual, since we want to be able to infer associated types from protocol extension implementations that themselves depend on the types being inferred a certain way, e.g.:
For this reason, we would collect potential witnesses from all possible extensions of the type without immediately checking the constraints on each extension. This could lead to an inconsistency where, when ranking potential witnesses, associated type inference would end up picking the "best" candidate as something that can't even be used by the type we're inferring the witness for, leading to incomprehensible behavior. The ranking criterion itself is also flawed, because
TC.compareDeclarations
is normally comparing candidates for the same well-typed function invocation, but during associated type inference we haven't even established the type system we're comparing candidates within. Witness candidates from extensions with mutually exclusive associated type constraints but otherwise more specific constraints would be unnecessarily dismissed as ambiguous because neither candidate's generic signature is a match for the other's.Both problems can be mitigated by looking at only the constraints for protocol extensions that specifically constrain the
Self
type, since those conformances are always explicit in the source code and therefore independent of associated type inference. We can pre-filter witness candidates by rejecting candidates for which the conforming type doesn't match the extension's Self constraints. We can score multiple witness candidates by looking only at the relative constraints they put onSelf
, considering a witness from a context that more specifically constrains Self to be a better candidate; we should ignore the associated type constraints for this scoring because we don't even know if they apply given the solution we'll ultimately pick. There's probably a more systemic way to go about this, but targeted fixes for both of these problems, along with a following patch to the standard library, restores source compatibility for Collection conformers, fixing rdar://problem/30228957.stdlib: Adding missing default implementations of subscript(Range) for combinations of [Mutable][RangeReplaceable][Bidirectional|RandomAccess]Collection.
These were overlooked, and somehow code that attempted to make a minimal collection conform to RangeReplaceableCollection and RandomAccessCollection managed to compile successfully in Swift 3.0, but in Swift 3.1…something changed to reject a type that conforms to both due to the lack of a suitable default slicing subscript implementation in the stdlib that provided all the requirements. Fill in these missing implementations. The type checker still fails to pick the right witness without an explicit
typealias SubSequence
, but this is progress toward rdar://problem/30228957.