Begging your pardon, but you didn’t identify why it would be invalid to do what I said it would be valid for the compiler to do. Without that, it’s hard to see what you’re disagreeing with.
I’m reasonably confident that if the assertion is of a concrete type, and applied to all return values of the subject (this being the proposed semantic), that one of three situations obtain.
One: The assertion matches return_types
exactly. In that case, the function is type stable, and also, the assertion does not need to be compiled into it, compilers are allowed to elide code which is known to be redundant.
Two: The intersection of return_types
and the assertion is Union{}
. This means that calling the function can only ever throw an error, and it would be valid for the compiler to inform the user of this with an error. Valid, mind you. I could be convinced that it would make compilation slow, but I’d need to hear the argument for that. It doesn’t matter if this code is type unstable or not, it’s almost an academic question to ask about a function which must throw an error.
Three: return_types
is a Union containing the assertion, or an abstract supertype of the assertion. This code is probably type unstable, and is either sometimes invalid, or it’s correct but the compiler can’t infer down to the type of the assertion. The compiler could detect some cases where the assertion can definitely fail, an example being when return_types
is a Union with the assert type as one member, and can warn about this, but since it doesn’t know that actual uses of the function will throw errors, it must compile. In other cases this be an inference failure, meaning it’s possible that the code is completely correct but return_types
is Any
, for one example. Here it shouldn’t warn either, just compile in the type assertion and proceed. Other tools can bring this to the user’s attention, as happens now.
We can contrast this with the current converting declaration, where the compiler can for the most part not detect an invalid combination. If return_types
is just UInt8
, and the annotation is ::Foo
, it can’t know if some later code might define convert(UInt8, ::Foo)
, so it has to just add the call to convert
and carry on accordingly.
Your discussion of what an independent static analyzer could provide is fascinating, and I agree it would be a good idea. I’m sure there are more expensive operations, like a bidirectional type synthesis, which it would be inappropriate to add to the Julia compiler itself, but which would help write correct code. I’d use it.
My unchecked assumption is that the compiler already uses return_types
on all methods, so it can use that information to generate good code at call sites, in which case, throwing an error when it detects an invalid assertion would be free, practically speaking, just a single added conditional. That could be completely wrong! It’s not worth making all compilation noticeably slower just to throw an error in situation two, but I do believe it would be valid to do so, if we accept the premise that the compiler is allowed to reject code which is known to be erroneous.