-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Generic function taking closure with inout parameter returns erroneous results #6586
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
Conversation
/cc @rudkx @slavapestov @jtbandes This is still work in process, but solves crasher related to the ticket and improves diagnostics related to result types of closures with anonymous parameters... I'm going to check why doesn't following type-check correctly right now (I'm pretty sure it has to do with how constraint graph contraction works related to parameter binding): func sr3520_2<T>(_ item: T, _ update: (inout T) -> Void) {
var x = item
update(&x)
}
var i = 42
sr3520_2(i) { $0 += 3 } |
I've figured out what the problem is last night but i don't know yet how to solve it properly - the reason why it's failing to type-check is because func f0<T, U>(_ t: T, _ f: (inout T) -> U) -> U {
var t2 = t
return f(&t2)
}
struct X2 {
func g() -> Float { return 0 }
}
_ = f0(X2(), {$0.g()}) since first argument of closure doesn't have to be I've tried to check materializability requirements on both sides and skip contraction if they are different but that breaks a bunch of other cases... Edit: I've incorrectly stated that constraint kind is 'arg conv' where it's actually 'subtype', because closure parameter bindings are constructed in context of function call, which means them subtype constraints, see |
@xedin We were discussing this problem yesterday because it also came up with #6608. @DougGregor and I had the idea that there should be one type variable for the internal type of each parameter (which is an lvalue type for inouts, an array type for varargs, etc) -- and another type variable for the external type of the parameter list (which need not be materializable). A constraint binds the outer type variable with a tuple type of inner type variables, enforcing the necessary conversions. What do you think? |
@slavapestov That's what I was kind of thinking as well (but less general)... I thought that maybe if one side of the contractable constraint must be materializable but the other isn't instead of contracting constraint it would make more sense to create a separate binding which is going to reverse the relationship e.g. -> All that said, I'm not yet sure if that's going to work at all, since I haven't tried it but in theory it should enable t1 to be bound properly and doesn't prevent related 'bind param' constraints from being contracted... |
Perhaps @cwillmor would have thoughts on this; although no longer a maintainer I think he was the original author of BindParam constraint stuff... |
@slavapestov @DougGregor I've pushed a fix which deals with Please take a look and let me know what you think, I've added comments and tests for all related issues SR-3520, SR-1976, SR-3073, SR-3479. |
Awesome. So the vararg problem is not related after all? |
@swift-ci Please smoke test |
Yeah, vararg is unrelated, it has to do with contraction of subtype constraints, generated by sub-declarations of trailing closures to be precise :) |
// (if inout containts type var, see ConstraintGraph::contractEdges()), | ||
// since right-hand side type variable must not be materializable | ||
// it can simply get left-hand side as a fixed binding, otherwise fail. | ||
if (type1->is<InOutType>() && type1->hasTypeVariable() && typeVar2) { |
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.
Maybe this should be type1->getInOutObjectType()->isTypeVariableOrMember()?
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.
Sure, let me change it to check for type variable directly instead of presence of one.
Convert subtype constraints with inout on one side and type variable on the other into fixed binding. Constriants like `inout T0 subtype T1` where (T0 must be materializable) are created when closures are part of the generic function parameters e.g. `func foo<T>(_ t: T, (inout T) -> Void) {}` so when such function gets called e.g. ``` var x = 42 foo(x) { $0 = 0 } ``` it's going to try and map closure parameters type (inout T0) - where is opened generic parameter T - to argument type (T1), which can be 'inout' but it's uncertain at this stage, but since closure 'declaration' `{ $0 = 0 }` is wrapped inside of a function call, it has to 'map' parameters to arguments instead of converting them, see `ConstraintSystem::matchFunctionTypes`. Resolves: SR-3520, SR-1976, SR-3073, SR-3479.
@slavapestov Force pushed the change for type variable checking, thanks! |
@swift-ci Please smoke test |
@swift-ci Please smoke test Linux |
1 similar comment
@swift-ci Please smoke test Linux |
@slavapestov previous Linux build is still going, just got to run tests, I took a long time to compile llvm/clang and other pieces... |
@swift-ci Please smoke test Linux |
\o/ |
@slavapestov Thanks again! |
@slavapestov Will you resolve the tickets or should I? |
@xedin I went ahead and resolved them, thanks! |
@rudkx Awesome, thank you! |
This didn't make Swift 3.1? |
Resolves SR-3520.
Resolves SR-1976.
Resolves SR-3073.
Resolves SR-3479.