Dynamic assignments to union and intersection types
See original GitHub issueI’ll use the following declarations in this issue:
dynamic X { shared formal String x; }
dynamic Y { shared formal String y; }
This code doesn’t work:
X|Y xy;
dynamic { xy = dynamic [y = "y";]; }
The compiler completely ignores the union type and treats it like an assignment to type X
. The possibility that the correct type might be Y
, and that it should check for Y
’s members too, is completely ignored.
This code does work:
X&Y xy;
dynamic { xy = dynamic [x = "x";]; }
Again, the compiler ignores the intersection type, and only checks assignability to the first type. y
remains undefined
; attempting to use it (which, to the typechecker, is perfectly legal) can result in a nasty surprise.
The first case (union type) is problematic because, in general, it’s impossible to tell which type of the union is the right one. Which type should this assign to the value?
X|Y xy;
dynamic { xy = dynamic [x = "x"; y = "y"; z = "z";]; }
X
? But whyX
and notY
?Y
? Same question.X|Y
? This breaks a coverage/enumeration guarantee of the language: aswitch
with casesis X
andis Y
is exhaustive becauseX|Y
(union of case types) coversX|Y
(switched type), but here our value is neitherX
norY
.X&Y
? This seems intuitively natural, but I don’t know how the typechecker would arrive at this type. I also doubt that such a solution exists for every similarly problematic situation. This also breaks the contract ofceylon.language.meta::type
and violates a language guarantee of §8: “In Ceylon, every value is a reference to an instance of a class”.
The second case (intersection type) is less problematic, but the issue of the value not having a non-composite type remains.
Issue Analytics
- State:
- Created 7 years ago
- Comments:15 (15 by maintainers)
Top GitHub Comments
I propose that both assignments should be disallowed. Instead, force users to write code like this:
It’s relatively easy to fix for intersection types: the object simply has to contain the members of all the types in the intersection. Although probably an anonymous type would have to be created at runtime, which could create problems if metamodel is used on it.
Union types are tricky though, and don’t even seem to make sense for
dynamic
objects. I don’t know if it can be disallowed on the typechecker; otherwise it would have to be disallowed at runtime.Now, if we change the way this whole thing works and instead of dressing the objects we simply let them go and make
is
check for members on dynamic objects, the waydre$$
currently does, then assigning dynamic objects to union/intersection types would work just fine. I don’t see how this will break binary compat, since thedre$$
function will still exist, but will only check the members without actually adding type info to the object, and so can also be used insideis
. Perhaps this would be the best way to go; it makes sense in this context. But I can’t remember right now if there were other reasons why the object needed to be dressed up at the site of assignment; probably something to do with leaking untyped objects from dynamic blocks, even if they are checked to see if they have the required members to pass for some typed object…