Soundness issue with intersection types #35100
Labels
legacy-area-front-end
Legacy: Use area-dart-model instead.
type-bug
Incorrect behavior (everything from a crash to more subtle misbehavior)
The document subtyping.md states that 'Promoted type variable types will only appear as top level types: that is, they can never appear as sub-components of other types, in bounds, or as part of other promoted type variables.'
However, that property is not enforced consistently by the current static analysis. Consider the following program:
This program produces a 'No issues!' response with
dartanalyzer
and withdartanalyzer --no-implicit-casts
(version 2.1.0-dev.8.0 as well as a fresh one from commit 55f3cc9), it has no compile-time errors withdart
(same versions), and it throws as follows ata.bar()
:Note that before the dynamic error occurs, the program prints 'Just assigned a string to a variable of type A, still running', which shows that a heap invariant violation has taken place (we assigned an instance of
String
to the variablea
of typeA
) and no dynamic errors were incurred when we did that.The underlying issue is that the static analysis allows the static type of the variable
x
at the invocation oftypedResult
to propagate into the type of the invocation: Presumably, the actual type argument passed totypedResult
is considered (by the static analysis) to beX & A
, and the returned value is considered to have typeList<X & A>
, because that's the reason whydartanalyzer --no-implicit-casts
considers the assignment ofv[0]
toa
to be type correct.If the static analysis had considered the type argument to
typedResult
to be a plainX
then the type ofv
would have beenList<X>
, anda = v[0]
would have been a compile-time error (with or without--no-implicit-casts
).If the static analysis had considered the type argument to be a plain
A
thenv
would have had typeList<A>
anda = v[0]
would be OK, but we just need to add inX v2 = v[0];
: That would then have been a compile-time error, and it is not (and the fact that it still isn't an error with--no-implicit-casts
shows that the type ofv[0]
is not a supertype ofX
or a supertype ofA
).The soundness issue arises because the static analysis fails to track the dynamic semantics faithfully: The actual actual type argument passed to
typedResult
isObject
, that is, code is generated to passX
(vary it a bit and print it), but the static analysis thinks that the type argument isX & A
, and when they are not the same type we cannot trust various kinds of code (say, type casts, or newly created objects withY
in their reified type, or invocations of other generic functions receivingY
as an actual type argument or as part of one).Apart from the obvious soundness issue that we get to assign a
String
to a variable of typeA
, we also have the issue that the type argument passed totypedResult
has boundA
, but we get to actually passObject
(no static error, no dynamic error), and it seems impossible to avoid the conclusion that the inferred type ofv
isList<X & A>
, which is a shape of type that shouldn't occur anywhere at all.So we definitely need to avoid the split where a type argument passed to a generic function call is considered to have one value during static analysis and actually gets another value at run time. It's hard to tell what this means for existing code, but it might be necessary to pass type arguments explicitly in some situations where the inferred type argument now cannot be an intersection type.
The text was updated successfully, but these errors were encountered: