language icon indicating copy to clipboard operation
language copied to clipboard

Should static extension allow generative redirecting constructors?

Open leafpetersen opened this issue 1 month ago • 8 comments

We currently propose to allow static extensions to declare generative redirecting constructors. However, because constructors on static extensions may return instances with more specific types than their static type, we currently propose to forbid such a constructor from being used in another redirecting constructor call, or a super call. In comments here, @stereotype441 makes the following point:

If we're not going to allow a constructor declared in an extension to be used as a super-initializer, or as the target of a redirecting generative constructor, then what's the point of allowing an extension to declare a redirecting generative constructor at all? Is it just so that an extension can declare a const constructor?

If so, I'm not sure that carries its weight. I would be more inclined to prohibit an extension from declaring a redirecting generative constructor, and if at some future point we decide that we want extensions to be able to declare const constructors, we do so by a more general feature that allows any factory constructor with a potentially constant body to be declared const.

As currently specified, generative constructors in static extensions are only supported for the const use case. Should we instead disallow them for now and consider the more general solution?

cc @dart-lang/language-team

leafpetersen avatar Nov 05 '25 01:11 leafpetersen

From my conversation with @alexmarkov, allowing generative redirecting constructors in extensions incurs some work on the part of the backends.

chloestefantsova avatar Nov 05 '25 13:11 chloestefantsova

The use-case for extension redirecting generative constructors is being const and allowing evaluation of (potentially constant) expressions before invoking the existing generative constructor.

extension on Symbol {
   const new setter(String name) : this('$name=');
}

This cannot be done with a factory constructor.

I think we should allow this, even if we don't allow such a constructor to be a the target of another generative constructor, whether super-constructor or redirection target.

We can do those other things, but that requires some more restrictions.

lrhn avatar Nov 05 '25 15:11 lrhn

My inclination is to disallow all kinds of generative constructors in extensions. You get factory and const factory, but that's it.

I understand that eliminates some edge use cases around const constructors that do some limited amount of computation. I'm willing to sacrifice those to avoid all of the very confusing complexity in #4559 and #4560. I'm just so tired of adding more complexity to random corners of the language to deal with the limitations around constant evaluation.

If use cases like this one really matter, I'd much rather spend that complexity adding actual compile-time evaluation to the language instead of a long tail of little one-off constant features. Then you could just write a const factory constructor with a body and be done with it.

munificent avatar Nov 05 '25 23:11 munificent

Both proposals are interesting:

@lrhn wrote:

I think we should allow this

It is indeed very tempting: It adds a small amount of computational abstraction to constant expressions, enabling simpler constant expressions and better correctness support (there's a greater danger that a complex expression will contain typos).

@alexmarkov, do you expect that the backends will need to change in order to support the use of redirecting generative constructors in extensions only as the direct target of a constant expression which is an instance creation?

@munificent wrote:

My inclination is to disallow all kinds of generative constructors in extensions

This is an attractive approach because it's safe and simple, and we can add the feature later on.

I think the feature would be useful (and welcomed by the community!), but right now we can go either way.

eernstg avatar Nov 07 '25 15:11 eernstg

@eernstg We do not need to extend back-ends only in one case: if a new language construct can be fully expressed / lowered to other existing language constructs.

According to @johnniwinther , the current plan for supporting redirecting generative constructors in the extensions is converting them to top-level functions which would behave as factories. Ordinary calls of such functions are definitely supported, as long as we don't support calling them via super and this from other constructors and don't support calling them via Dart C API (Dart_New) or via dart:mirrors. If we allow taking tear-offs of such constructors, there could be certain edge cases which may require back-end support (depending on semantics of such tear-offs around type arguments and/or lowering of such tear-offs).

However, currently we don't support constant evaluation of top-level functions, so we will need to enable const-functions experiment for them. Even this small change in semantics would require a small change in the back-ends: we would need to handle these new const top-level functions properly when exporting them through a dynamic module interface (similarly to const constructors, everything mentioned in the bodies of such functions should be implicitly exported as it could be referenced by a dynamic module).

I think we should not be afraid of making back-end changes as long as we have enough bake time. We should just stick to what is best for our users.

alexmarkov avatar Nov 07 '25 16:11 alexmarkov

What of er extended the functionality of redirecting factory constructors to allow

class Foo {
 const factory Foo.name(args) 
     : SubFoo<T>.otherName(otherArgs);
}

The otherArgs must be potentially constant expressions.

It's like a redirecting generative constructor, but doesn't have to only target the same type.

Then there would be no functionality that a redirecting generative constructor can do, and that a factory cannot, if you're not using it as a target of another generative constructor.

And then it would be no issue not allowing a cc generative redirecting coordinator in extensions, if it can't be used as a generative constructor target anyway.

Could also choose to not introduce new syntax, and allow

const factory Foo(rags) =>
    SubFoo<T>.name(otherArgs);

where we allow a potentially constant constructor invocation in tail position. (Which is probably more to work to allow than just adding new syntax.)

lrhn avatar Nov 09 '25 12:11 lrhn

> However, currently we don't support constant evaluation of top-level functions, We actually uses this code path to support constant constructors in extension types, which are also lowered to top-level functions, so supporting constant redirecting generative constructors in the CFE does not require anything new.

johnniwinther avatar Nov 10 '25 12:11 johnniwinther

I would like to suggest allowing

const factory Foo(rags) =>
    SubFoo<T>.name(otherArgs);

instead of introducing more syntax.

Wdestroier avatar Nov 10 '25 13:11 Wdestroier