Skip to content

Update static extension proposal with constructors#4537

Open
leafpetersen wants to merge 2 commits into
mainfrom
constructors
Open

Update static extension proposal with constructors#4537
leafpetersen wants to merge 2 commits into
mainfrom
constructors

Conversation

@leafpetersen
Copy link
Copy Markdown
Member

First cut at specifying constructors in static extensions directly. This handles all of the core semantics, but currently does not allow declaring constructors in an extension using a typedef name, and currently forbids using generative constructors declared in an extension as targets of a redirecting generative constructor or as a super constructor call in another constructor. We almost certainly should reconsider the former (either before or after landing this), and may wish to reconsider the latter.

Comment thread working/0723-static-extensions/feature-specification.md Outdated
Copy link
Copy Markdown
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

The most substantial comment is probably about constant constructors, they don't seem to be covered by the description of the dynamic semantics.

I have some comments about constructor names.

Finally, I added a bunch of change requests that are only concerned with formatting (some lines in source code blocks are too long, which is not good for readability). These are marked as "Format".

Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
in exactly the same manner as an invocation of a static generic method
in context `K`, the type parameters of which are the type parameters of
the extension; the return type of which is `R`; and the parameter
types of which are the declared parameter types of the constructor.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to cover the case where the constructor is constant and redirecting, in particular during constant expression evaluation.

Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
}
Constructors declared in extensions may not be used as
super-initializers, nor as targets of redirecting generative
constructors.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can they be use as the redirection target of a redirecting factory and, if so, can they be referenced through the on-declaration:

class A {
  A.a();
  factory A.b() = E.d; // Through the extension name. 
  factory A.c() = A.d; // Through the on-declaration.
}
extension E on A {
  A.d() : this.a();
}

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As specified now, A.d may not be generative, but could be redirecting. Do you have concerns with that?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll cause a bit more jumping around the declarations when resolving redirecting factories, since you would have to look up constructors in extensions that are in scope, but that should be doable.

Comment thread working/0723-static-extensions/feature-specification.md
Copy link
Copy Markdown
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the model works. I'm (as usual) not happy about specification by desugaring or explanations using "is treated like". (I know, I've done it too. That didn't go well either.)

Some of the terms confuse me, maybe because I'm not sure what category they belong to. (Is a "fully resolved reference" an expression or a semantic entity, and if the latter, shy does it have a syntax)?

I still think this can be implemented without too much confusion, by people who know what is intended, but I fear for the edge cases.

Comment thread working/0723-static-extensions/feature-specification.md Outdated
Comment thread working/0723-static-extensions/feature-specification.md Outdated
{...}` where `F` is a type alias whose transitive alias expansion
denotes a class, mixin, enum, or extension type `C`, we say that the
_on-declaration_ of `E` is `C`, and the declaration is treated as if
`F<T1 .. Tk>` were replaced by its transitive alias expansion.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this is defining the on-declaration syntactically, and doing type alias expansion to get to the final declaration. Which works, as long as we can resolve identifiers to declarations, and perform type argument substitution at the syntactic level.

Maybe we could instead say if the uninstantiated on type (the type denoted by the on type clause) is a type introduced by a class, mixin, enum or extension type declaration, then that declaration is the on declaration of the extension?
At least that bypasses the type aliases, and doesn't have to handle non-generic, raw and instantiated types separately.

It would mean that we can't know the on-declaration until we have ensured that declarations introduce types, and type expressions can be resolved to the type they denote, but I think we can wait for that.
(It's probably the step after being sure that all identifiers can be resolved to a declaration of the correct kind.)

Comment thread working/0723-static-extensions/feature-specification.md Outdated
For the purpose of identifying the on-declaration of a given
extension, the types `void`, `dynamic`, and `Never` are not considered
to be classes, and neither are record types or function types, with
the exception of the types `Record` and `Function`, which are
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really an exception, because they are not record or function types.
Maybe:

Both `Record` and `Function` themselves are classes.

(They are not just considered to be classes, they are classes.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But they are considered to be classes, so...? Is there a substantive change you're asking for here, I believe the text as is captures what I want to say.

Comment on lines +855 to +856
the un-instantiated function type of the constructor reference with
`TypeArguments` substituted throughout for `TypeParameters`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it's the instantiated function type, for the extension instantiated with the inferred type arguments?

generic function or constructor.*


## Dynamic Semantics
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need a section on tearing off extension constructors through an alias.

Most things are unchanged. If instantiated, it just works like tearing off from the instantiated type.

For normal constructors, a tear-off through an uninstantiated generic alias has the same type parameters as the type alias. The tear-off is identical to the tear-off directly on the class if the alias is properly forwarding type arguments. (Has the same type parameters in the same order, and forwards them directly to the class.)

For an extension constructor tear-off through an uninstantiated generic alias, the function would still have the same type arguments as the alias, but it should likely be identical to the direct tear-off from the extension if the alias has the same type parameters as the extension, and forwards them to the class the same way that the extension puts them into its on type.

I think the only change would be identity, basing it not agreeing with the target type, but with the declarer of the constructor.

### Dynamic Semantics of constructors defined in extensions

Declarations of constructors defined in extensions are semantically
treated as declarations of static methods with return type given by
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"treated as" again 😉 .
Are they, or are they not static methods?

That is: Does a syntactic constructor declaration in an extension introduce/define a static method, or does it introduce an entity which is not a static method?

If they are not static methods, we don't need to say that they are treated as static methods. Just say how they work, in its entirety, without falling back on "like a static function", because that will very likely leave some details unspecified that are not actually exactly like a static function.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you want here. The question at hand is "what are the dynamic semantics of these things". And my answer is "treat them as static methods - that is, give them the dynamic semantics of static methods". It is not relevant here whether they are, or are not, static methods. Syntactically, they are not. Semantically, they behave identically to static methods. Does that make them static methods? Neither I, nor the reader cares. Repeating the entire semantics of static methods would be a recipe for disaster. So what do you want me to say? "Extension constructor declarations are static methods." with nothing further is cryptic and unhelpful.

ordinary static member of the extension by treating it as if both the
type parameters and the on-type of the extension were copied down onto
the declaration of the constructor to serve as the type parameters and
return type of the static member*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or say:

A constructor declaration of an extension introduces a static method with the following
signature:

  • The same type parameter list as the extension
  • The uninstantiated on type of the extension as return type.
  • The same parameter list as the constructor declaration.
  • A fresh name, which we associate with the constructor declaration.

When a constructor invocation denotes the extension constructor declaration
When invoked as a object creation expression
with TypeArguments as type arguments list as arguments as argument list,
then bind the type arguments and arguments to the formal type parameters
and formal parametrs of the constructor declaration. Then

  • if the constructor declaration is a redirecting generative constructor
    with a redirecting clause this(args) or this.id(args),
    let C be the type-parameter instantiated constructor denoted by this or this.id,
    evaluate args to an argument list,
    and invoke C with that argument list as an object creation expression.
    The extension constructor invocation evaluates to the result of that invocation.
  • If the constructor is a redirecting factory constructor with redirection = T; or =T.id
    where T is a type clause, let C be the type parameter instantiated constructor
    denoted by T or T.id after type inference, with the type parameters of the
    extension replaced by the corresponding TypeArguments.
    Then invoke C with arguments as argument list as an object creation expression.
    The extension constructor invocation evaluates to the result of that invocation.
  • If the constructor is a non-redirecting factory constructor, execute the body in the
    scopes resuliting from binding actuals to formals.
    If the function body completes by returning a value, then
    the extension constructor invocation evaluates to that value.
    Otherwise the function body compled by throwing an error,
    and the extension construtor invocation threw the same error.

That is:
Say which semantic entity exist (a static function), what its properties are (function signature and name), and how it behaves when run.


#### Dynamic Semantics of fully resolved constructors

Invocations of fully resolved constructors are treated as invocations
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does not describe what happens for object creation expression (anything with a new or const in front). Those are not invocations, or if they are, they are special ("invoked to initialize object o").

We can probably say that redirecting generative extension constructors are treated "more like" forwarding factory constructors, but since they can do things that a factory constructor cannot, that's not a full description.

We can maybe say that an object construction expression invoking an extension constructor is not actually an object construction expression, it's just an invocation of a static function, and the body of that static function will then invoke another constructor as an object creation expression. That is, "more like a factory."

But that doesn't explain const invocations.

class C {
   final num i;
   const C(this.i);
}
extension on C {
  const C.add(num i, num j) : this(i + j);
  const factory C.sum(int i, int j) = C.add;
}

Neither of these constructors can be explained by desugaring to a static function, because they do things that no static function can do (even if only "be evaluated as const").

@leafpetersen
Copy link
Copy Markdown
Member Author

Ok, here's another crack at this, PTAL. I think most of the remaining comments are obsolete given the new structure, but I'll take a pass over them again (and if you have a comment which you think is still TBA feel free to give me a gentle nudge on it).


This situation is just an abbreviated notation for a declaration where the
constructor declares its own handling of genericity:
Constructors defined in generic extensions may also be invoked using
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it say "invoked or torn off" instead of simply "invoked" by analogy with lines 175-176?

section, using the same downwards context as the original expression.

*Inference serves to find the type arguments (if any) that are missing
from the fully resolved invocation. These arguments are what are
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it say "These arguments are what is needed..."?

Comment on lines +805 to +807
The static type of the constructor tearoff is `C<TypeArguments> Function(Signature1)`.

It is a static error if `M1` is not a subtype of `C<TypeArguments>`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In lines 805 and 807 should it say C<TypeArguments1> instead of C<TypeArguments>?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants