[Development] Redesigning QML value types

Ulf Hermann ulf.hermann at qt.io
Wed Sep 21 20:00:58 CEST 2022

> Should there be a way to pass by reference even when using type annotations?

Providing a way to pass by reference for type annotated functions will 
probably in people expecting such code to be compiled to C++. I would 
only provide such syntax if qmlcachegen and qmlsc can deal with it. If 
it's of high priority, we can go back to the idea of chained wrappers as 
representation for value type references, but currently I don't think 
it's the most important thing to do.

> 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.

Oh, I would actually be interested in your past assumptions on this. In 
general, it would be easy for the language server to see something like 
"this is a non-trivial value type pass as a (non-)annotated argument". I 
don't know off the top of my head if the language server protocol has 
any facilities for what you are envisioning.

> 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?)

The copying may be an issue. Mind that whenever you read a value type 
from a property using a getter you already copy once and every time you 
write it back you copy again. Therefore, I generally assume value types 
are easy to copy, and we can afford another copy when passing a value 
type as function argument. However, qmlcachegen and qmlsc are in fact 
rather clever about this. They will only perform the copy if you are 
going to modify the argument. Otherwise they will take a reference 
instead. Of course if you're calling from interpreted code into compiled 
code or vice versa we do have to copy.
> 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.

I agree to some extent. The main question here is how value types are 
supposed to work and one problem is that the behavior of the JS engine 
and the compiled code differ. A solution would indeed be to just drop 
support for compiling functions to C++ and keep the rest as it is. Since 
that support already exists, I would expect some backlash, though. Yet, 
indeed I'm very cautious about what features I implement in the compilers.

> 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)

Signals auto-detach value types. Remember that signals have always been 
typed in QML. Therefore, when sending a signal, the parameters are 
transformed into their C++ equivalents and the back reference to the 
property is lost. Even in C++, sending references through signals is at 
least somewhat adventurous.

You might, however, define a value type that simply holds a pointer to a 
QMouseEvent and has a Q_INVOKABLE setAccepted() method. That thing you 
can send and call wherever you like. You just have to take care that the 
pointer stays valid.

More information about the Development mailing list