[Development] Redesigning QML value types
Shawn.Rutledge at qt.io
Wed Sep 21 16:45:57 CEST 2022
> On 2022 Sep 21, at 14:59, Ulf Hermann <ulf.hermann at qt.io> wrote:
> Thanks for the feedback! I've thought about it some more and done some experiments and I think we can solve this in a way that makes everyone happy:
> If a function has type annotations, we pass its arguments and return value by value (if they are value types). Otherwise we pass by reference. Inside a function, everything is a reference.
Should there be a way to pass by reference even when using type annotations?
Could the language server tell us (so the editor can provide hover feedback or something) what’s passed by value and what by reference? I think now my understanding has been wrong a lot of the time, and may be in the future too.
In C++ some people are nagging constantly about using const-refs everywhere. But here it looks like you are in favor of passing by value. (We are supposed to use type annotations more, right? And then we get copying?)
> You also get to keep the convenience of "o.a = 10" and similar constructions this way, and we can seemlessly extend that to more deeply or differently nested structures.
> Remember that qmlsc and qmlcachegen only compile typed functions to C++ and the generated code will also only call typed functions. This means, with the above proposal, our AOT-compiled functions don't have to pass value type references as arguments or return types. Therefore, we can leave the compilers as they are. Otherwise, if we had to pass value type references instead of actual value types between our AOT-compiled functions, their signatures would have to change. That would have user-visible effects on the metaobjects. Plus, if we were to return value type references from functions, we'd have to allocate at least some of those value types on the heap, and garbage collect them at some point. I feel we really shouldn't go there.
> The downside is that we have to swallow a behavior change in interpreted code: So far the QML engine passes everything by reference and now it will have to detach the arguments and return values when calling or returning from a typed function. However, since the primary reason to annotate your functions is to get them compiled to C++, and since qmlsc and qmlcachegen already generate code that behaves the new way, this may not be all that hard.
I mostly think of our JS engine as being something to minimize the use of, at least in nontrivial applications. (Yeah writing single-qml-file apps is fun; only then do I actually want to use JS in my QML.) If we can never leave out the runtime, even when using the compiler or even when we manage to write pure-declarative QML, then it should be kept as small and simple as possible. Sometimes small size gives you much more speed than you expect, just by fitting better into CPU cache. (And if we were free to pick a tried-and-true small language, it could be even smaller.) So I don’t know how much it’s worth changing things for performance. QML is declarative first, so I tend to think optimizing the size and creation time of the objects that are declared is more important than compiling the little bits of imperative code that you shouldn’t write in the first place.
Being able to consistently set properties on nested value types and value types in lists, in-place, sounds like a good thing to get working.
If we can pass Q_GADGETs by reference (properties and signal arguments)… you know I wanted that for a few years, but I thought it’s supposed to be incompatible with having them as value types. But if value types are often passed by reference anyway, why is it again that I can’t emit a QMouseEvent and let the QML user set the accepted flag? (As ugly as that is)
More information about the Development