Hi Martin,

Martin Uecker <[email protected]> writes:

> you seem to take it for granted that it templates improve
> maintainability, but in my personal experience the opposite
> is true.

No, my hunch that the (currently pre-draft) proposal would improve
maintainability rests on the experience in the JavaScript and Python
cases, in both of which the introduction of gradual typing systems
massively increased the ability to detect bugs ahead of runtime,
approach new code, and modify old code.  It does not rest on the
presence of templates.

Templates are merely a required means by which a similar type system can
be implemented for GCCs 'tree', in GCCs implementation language - C++.

The statement "templates improve maintainability" is not really a
sensible position to hold, just like "templates reduce maintainability".

Templates are merely a means of writing generic functions or data
structure.  Doing so obviously can be both an improvement in
maintainability and a hindrance to it.

Which it is depends on the template.

>> We already have a relatively small base of active developers, and a yet
>> smaller base of reviewers.
>
> I am not sure that adding more complexity would help with this.

Exactly how almost no TypeScript user needs to learn how to hack the
TypeScript compiler, and how almost no Python user needs to learn how to
hack Mypy et al, and how most C users don't need to learn to hack GCC to
write C, almost no GCC developers will need to look into the tree type
checker.

That's not to say they shouldn't.  There's just no reason to presume
they often will.

> Speaking for me, I would not be able to contribute anymore if build
> times triple.  It is already a pain today and I have fairly fast 
> machines available to me.  

I certainly wouldn't expect build times to triple from such a change ;)

Iteration time for me does not tend to be too excessive, even when I'm
just using a core i7-8650u or ryzen 7 3800x.  I'd not call them weak,
but they certainly aren't top-notch.

>> 
>> That said, there's no reason why we couldn't make it possible to
>> essentially disable the 'tree' type checking.  I don't think that's
>> worth it, frankly, and I think it adds to the combinatorial explosion
>> that is GCC build-time configuration, but we could explicitly make it so
>> that 'ttree' with, say, checking disabled, compiles down to:
>> 
>>   template<typename...> using ttree = tree;
>> 
>> Hence, short-circuiting all or nearly all cost.
>
> If it was my call, I would start by introducing simple wrapper types
> for expressions, types, statements, .. and also only for some parts 
> of the code where this makes sense.  This would add static
> type checking and readability (!) without adding any cost in terms of
> compilation time.

This runs into the problem of there being three million existing lines
of code.  The frontier between "new-style" code and "old-style" code
would need to be very explicit, and ergo, difficult to move.

This also runs into the problem of the countless cases in GCC where a
function can legitimately accept more than one type of tree, say A and
B.  There's no way, in C and C++, to declare a variable/parameter of
type A|B, which is something that does happen in GCC, even where for A
and B the lowest common supertype is 'tree' (using this common supertype
also permits a tree 'C' to be passed, ergo under-constraining).

This, also, sounds to me exactly like what Jakub was worried about in
the other subthread.

On readability: I'm not sure what you imagine, but there's no reason
readability ought to be an issue.

In each location where 'tree' is listed as a parameter or declared as a
local, one would also list the expected tree codes or similar, just as
they do today (except in the declaration instead of in an assert below a
declaration, which seems to me like a strict improvement, as it moves
relevant information from function bodies to function signatures,
without relying on bit-rotting comments; where there isn't such a check,
'auto' suffices).

Checks-and-uses of TREE_CODEs would also need to become (explicit)
fallible conversion functions, i.e.

  if (TREE_CODE (t) == A) use t;

... becomes:

  if (auto tA = try_as_tree_or_whatever<A> (t)) use tA;

... or such.  (I'd have preferred if the 'tA' could shadow 't', but
unfortunately if it were to shadow 't' in the then branch, it'd also
have to shadow it - a lot less usefully - in the else branch; that's not
an issue in this trivial example but is in general)

>> When I was initially thinking about this problem, I did put this down as
>> a design goal for a different reason: showing that GCC would compile to
>> the same machine code anyway even with this typechecking added, thus
>> making it easier to approve the patch series.
>> 
>> I'm now of the opinion that this original goal of mine isn't worth it,
>> especially as we could use 'ttree' to provide member accessors to
>> increase the level of assistance that can be provided by editors and/or
>> decrease verbosity (discovering what the correct accessor is for some
>> part of some tree_code is a bit of an art even today; this also did come
>> up in the C++ NAS patch, where TREE_LANG_FLAG_... was being used
>> incorrectly - this, too, could've been prevented, but luckily Thomas
>> caught it in review).
>> 
>> Of course, the latter is also impossible in case we do this short
>> circuiting for sake of compilation time.
>> 
>> I really do wish I had a partially done example, so that we could do
>> some empirical study on this.  But, alas…
>> 
>> > I agree that catching errors at compile time is good, but in
>> > many cases this can be achieved in C just as well.  I usually find
>> > that people underestimate what can be done in C alone.  Clearly, C++
>> > allows you to do more sophisticated typing (I wrote for some time
>> > expression-template libraries before I decided that this just takes
>> > too much of my time). Being clever with C++ templates is - in my
>> > experience - usually a bad trade-off as the cost in complexity and
>> > compilation times exceeds the improvements in type safety you can
>> > achieve in this way.
>> 
>> Please implement a conversion operator in C.  Adjusting 'tree' is a
>> non-starter without those.
>
> In my experience, having implicit conversions makes it harder
> to read and maintain code.  

Indeed, I agree.  Implicit conversion are in general not good.  But
user-defined implicit conversions are the only way to implement gradual
typing in a statically typed language, or at least the only one that
comes to mind.

We need some way to tell the $host compiler "okay, this conversion is
sound", "okay, this conversion *may* be sound, but perform this
assertion at runtime", "okay, this conversion is never sound, fail to
compile".

The only way to do that are user-defined conversions, and generic ones
at that.

A user-defined conversion template, at compile-time, knows the start and
end type, and ergo has sufficient information to implement custom typing
rules like those.

>> 
>> Then, we'll also need packs of types and of heterogeneous values that
>> can be manipulated at compile-time, to represent sum types.
>> 
>> > I agree, but the untypedness of tree is not the fault of C...
>> 
>> I agree, but I never said that it was.  Just that we can't fix it in C
>> in a viable way.
>> 
>> > ... and changing this would not require switching to C++.
>> 
>> It would.  There is no method by which we can implement correct rules
>> for gradual typing in C.  Especially not earlier versions of C.
>
> Gradual typing can also be done in C.  For example, here
> is a variadic type: https://godbolt.org/z/d8djYsMx5
>
> (And in this example, the compiler completely removes all
> overhead for the dynamic type checking and devirtualizes all
> functions)

This response makes me think that we're talking past each-other.

What you showed seems analogous to our starting point today, not the end
point I'd like to see.

Indeed, considering 'variadic*' analogous to 'tree' in your example
would, in terms of type safety, be exactly equivalent to what we have
today (well, except that "variadic*" can be anything, and trees can
"only" be anything packaged in a tree node).

What I want is to be able to specify "okay, this 'variadic*' [or tree]
can only be X, Y or Z, or ..." in a declaration, and have the compiler
verify that as well as it can, while still being somewhat compatible
with existing code.  This serves the purpose of self-documentation and
type safety.  (note that we're going from "tree" -> "one of ... trees",
rather than going from a concrete type to a less specific one).

It'd be nice if I could, say, convert the C FE to what I'm imagining in
time for Cauldron, but I fear that may be optimistic.

Doing so would eliminate all ambiguities and perhaps even alleviate
concerns.

Also, of course, what the correct thing to do here is can only be
learned through practice - abstract ideas come from practice and must
return to practice.  I'm sure I'm overlooking something in abstract
thinking; updating actual code (a "mere" 100k LoC frontend) would mean
that practical considerations have to be taken into account.

But, I certainly won't be able to work on this during work hours, and
most of my "free" time is anything but.. so I can't be sure that I can
finish it by then.  As I said, it's not simple.

(also, should 'variadic' be called variadic?  and not 'any' or
 'variant'?  I may be missing something, but I do not understand the
 naming.  This also isn't ISO C, whereas this can be done in ISO C++)

>> 
>> > It would simply require to introduce C types for each specific node
>> > type.
>> 
>> The "simply" here is doing a lot of heavy lifting.  There's nothing
>> "simple" about changing *the* core type of a 3 million LoC codebase.
>
>> 
>> As I said, gradual typing is the only alternative to dynamic typing
>> possible for GCC today.
>> 
>> C lacks user-defined conversions; this alone makes it impossible to
>> implement gradual typing in C.
>
> You can implement conversions just fine, you can just not have
> implicit conversions.

By conversion I'm of course referring to user-defined implicit
conversions.  User-defined explicit conversion are just unary functions,
and obviously C has those.

C does have too many implicit conversions already.
-- 
Arsen Arsenović

Attachment: signature.asc
Description: PGP signature

Reply via email to