[Development] Feature freeze exception for QTBUG-95587

Ulf Hermann ulf.hermann at qt.io
Tue Sep 14 13:15:44 CEST 2021


> I am wondering if the change was a good idea at all then. To me, it 
> feels like the change at the root of all this was not well thought out. 
> If it is expected that this change is going to hit so many users and 
> they need some facility to make it easier on them than littering their 
> code with Qt.resolvedUrl, it would perhaps have made more sense to go 
> the other way around and introduce an unresolved URL type, which can 
> then be resolved to a QUrl where needed without touching the default. 

A separate type would not help here. You would have to change your code 
in order to use it. If you do so, you can as well write 
Qt.resolvedUrl(). Furthermore, the "string" type can already be used to 
store unresolved URLs today.


> It feels like the support introduced for the more edge case use of 
> unresolved URLs as the default mode has ended up causing problems for 
> all those use cases where resolved URLs are needed. I don't _know_ what 
> discussion there has been around that at the introduction of this 
> change, but it _feels_ like it might be the result of a rushed in change.

We may have not made the discussion public enough, but I can assure you 
the decision was not rushed. There were many bug reports on the 
inconsistent automatic URL resolution in Qt5, and we've been trying to 
improve it for years. In the end it turned out that the only consistent 
solution is not to automatically resolve URLs. When Qt6 came around we 
saw the chance to do exactly that.

Usually, you set a property in the same scope as you create an element. 
So, all those

Image { source: "foo/bar.png" }

are unaffected. It doesn't matter if the Image element resolves the URL 
in its scope, or if the QML engine resolves the URL in ... the same 
scope. Technically, most user code does not need to change.

The case that hasn't seen enough consideration is the mechanism employed 
by QtQuick.Controls in some places, where the URL is exposed via an 
alias property. There, the scopes are in fact different and you have to 
resolve the URL explicitly. These places do need to change.

 From my point of view, those cases still don't outweigh the abundant 
problems we had in Qt5.

Yet, the fact that you need to convert in some places, but not in others 
is not ideal. Such subtleties make it hard to reason about the code in 
question. The need for resolving is not apparent from the immediate 
context but requires examining the internals of the element that 
receives the URL. Furthermore, the places you need to change should be 
automatically detectable by tooling so that we can, for example, make 
qmllint show you the places where you have to resolve.

The core of the problem is our implicit conversion from string to url. 
At the point where we convert, we don't know whether we should resolve 
the url in the current context or not. We cannot know because we don't 
know what the URL will be used for and whether it is already absolute. 
The user might just store the URL to be passed somewhere else or used 
for further conversions. In this case we should not resolve it. Or the 
user might want to load some resources from the URL. In this case we 
should resolve it. However, if it is already absolute, it doesn't really 
matter.

In order to make code that uses URLs more accessible to reasoning and 
tooling, we _need_ the user to specify. Right now, there are three ways 
to specify:

- The well-known Qt.resolvedUrl(url) explicitly resolves the URL in the 
current context.
- The new Qt.resolvedUrl(url, context) explicitly resolves the URL in 
the given context.
- The new Qt.url(url) explicitly doesn't resolve.

Given all this, we should eventually deprecate the implicit conversion 
and transition to the explicit ones. Then we can make qmllint flag every 
implicit string-to-url conversion and suggest the above three 
alternatives. I know we need to wait a minor release or two before 
adding a deprecation warning, but from a language perspective, this is 
the way to go. It will make your QML code more legible and you will 
immediately see where each URL is resolved.

Now, we can observe that the common case of explicitly resolving in the 
current context is rather verbose. Therefore, I imagine a shorthand 
would be useful. That's where the idea of the '@' operator comes from.

> Perhaps something along these lines would be possible:
> 
> A URL in QML can be resolved or unresolved. If a URL is constructed for 
> a string (like we have been doing for ages in QML), it is directly 
> resolved automatically. If you need an unresolved URL, you use another 
> syntax to construct the URL setting some flag on it to indicate it needs 
> to be resolved at some later point using another context. Syntax could 
> be either some constructor function (like Qt.resolvedUrl()) or perhaps 
> as a fully fledged type `Url{url: "someUnresolvedUrl"; unresolved: true 
> /*=default*/}`.

Mind that the URL is not resolved on _creation_ in Qt5, but rather when 
assigning to a property. We've actually pondered adding a "context" to 
QUrl itself, but in the end we've discarded the idea because it would 
make QUrl's comparison operators unintuitive. You'd get change signals 
for overwriting a URL property with a URL that looks equal, but is not.

See https://codereview.qt-project.org/c/qt/qtbase/+/369534 and 
https://codereview.qt-project.org/c/qt/qtdeclarative/+/369692

Furthermore, we'd re-introduce part of the original problem. What 
happens if you receive a URL with a context and assign it to another url 
property? Do you keep the original context or do you change it? Both is 
wrong without knowing what the user intends to do with the URL.

And, we have Qt.url() now to explicitly construct an unresolved URL.

best regards,
Ulf


More information about the Development mailing list