-
-
Notifications
You must be signed in to change notification settings - Fork 48
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
Unsound matches with unconstrainted type parameters of different caps. #172
Comments
This is related to exhaustive match. When a match is exhaustive, the We need to step through this and figure out how to properly handle this case without jumping to the |
@jemc 's diagnosis is right. @Praetonus says we should set the |
Sounds right to me 👍. |
Hm no, the unreachable solution doesn't seem correct. I'll investigate more. |
Since an equivalent code without generic types results in a compilation error, I think this match should either result in a compilation error or be considered non-exhaustive. I'm not sure which one is correct. |
@Praetonus - it's definitely an exhaustive match, so it probably needs to be a compilation error. Note that the error message you get when removing the indirection of the type parameters/arguments is:
Note that So, exhaustive match doesn't really have anything to do with that problem - it would be the same problem for a non-exhaustive match. This is an interesting case, because the only way to make this match safe would be to prove that either Perhaps what we need are some new possibilities for expressing constraints? I'm not really sure what that should look like. |
Sync consensus is that we want to remove crashing aspect of this (the bug) without disallowing valid code. |
This was discussed on the 2017-09-06 sync call. It's important to mention that this wouldn't be a problem if capabilities were present at runtime, rather than being a zero-cost abstraction that falls away at compile time. @sylvanc brought up that if we could still make them trivially cheap at runtime, this could be a good option, though we'd have to weigh it carefully because the zero-cost-abstraction part of capabilities that we have right now is really nice. In the absence of that, we have to come up with a solution that disallows the unsafe case, even if it may also disallow some safe cases. Our possible solutions are constrained by two requirements that we agreed on:
With these in mind, we agreed our best choice for action was to modify the After we explored a lot of options, the only feasible one we came up with was to change the compiler to assume that an unconstrained type param cannot be guaranteed to match any type, because we have no clue what the type argument is, and for any given type on the other side of the match, there are examples of type arguments that will be a match that "violates capabilities" in the way described in my previous comment. In other words, we should return I'm marking this as an "easy" task, since now that it has been fully discussed, the solution is quite simple. |
Marking as needs discussion because this change has implications for the We should consider a possible special case for |
During the last sync meeting we found that the failure to compile |
Right, and just to follow that up, it spurred me to add #105. |
Found another case where the compiler was allowing matching on capability only, due to an unconstrained type param: https://github.com/ponylang/ponyc/pull/2289/files#diff-35e468e76bf0c02622ffcf7465e7cf82L411 |
I'm not sure the implementation solution described by @jemc above is correct, since it would forbid matching from a type parameter to the same type parameter (e.g. if the match type is a union containing the type parameter.) I think a better solution would be to allow the match if the operand and pattern refer to the same type parameter with compatible caps, and to match on |
That’s a problem with structural interfaces. With (nominally or structurally selected) type classes which in my opinion would be preferable to interfaces and traits, AFAICT you wouldn’t have this problem. If I remember correctly, it was @keean who taught me that (in his opinion)
Are y’all familiar with Stephen Dolan’s MLsub thesis? He points out that type systems that have unions and intersections and that don’t put the unions and intersections (all types) in the ground types have an unwieldy algebra that is very difficult if not impossible to prove sound. Perhaps §2.1.5 Distributivity and subtyping on pg. 21 is also applicable. I’ll quote:
So if all types are in the ground types, then there’s a distributive lattice and I think the disjointness you need is guaranteed? I need to reread that MLsub paper to be sure about this. If the above is correct about your design being fundamentally unsound, then more cases will be discovered as more Pony code is written. And special case “fixes” would proliferate. In that case, perhaps it would be wise to bite the bullet now and scrap current design for a design that would be sound? Especially if the replacement would provide superior functionality and expression for the programmer. EDIT: I posted that at 3am my time. After thinking about this more, nominal subtyping (e.g. traits) also prevents the needed disjoint invariant of the types (e.g. Also anonymous, structural (i.e. not nominal) intersection types seem to cause problems in a type system. If there’s no traits nor interfaces, then I don’t think they’re needed (not accessible to the Pony programmer) except perhaps only in the type checker as the dual to anonymous, structural union types. @keean had demonstrated to me that intersections of first-class functions can cause unification to be undecidable. We see another example of unsoundness due to intersections in §2.6.4 Modelling Challenges of the recent paper by George Steed about improving Pony. FYI: this post of mine might subsume the Subtyping exclusion RFC. EDIT#2: some additional thoughts. Note that to exclude the vacuous reasoning, I posit not only would need to exclude traits and interfaces (i.e. subtyping other than for anonymous, structural unions) from the programming language, but would also need to exclude anonymous, structural unions from type parametrised cases that can cause undecidable cases such as the example of this thread. I think that could be accomplished by only applying type class bound operations on anonymous, structural unions, which effectively removes any overlapping non-disjoint portions of any union of said unions because the type class instances are resolved at compile-time and can’t be overlapping.¹ Understanding my posited point may be aided by reading this. ¹ Haskell does have an optional extension for overlapping instances but presuming not enabling overlapping instances. |
Compiler crash on following code:
Backtrace(lldb):
NG patterns give same crash.
Compiler version:
The text was updated successfully, but these errors were encountered: