Skip to content

Pin why module bounds cannot be fixed by rewriting the receiver - #1246

Open
Frotty wants to merge 1 commit into
masterfrom
feat/module-bounds
Open

Pin why module bounds cannot be fixed by rewriting the receiver#1246
Frotty wants to merge 1 commit into
masterfrom
feat/module-bounds

Conversation

@Frotty

Copy link
Copy Markdown
Member

module M<T: Show> is rejected today. Backlog item 7 proposed two ways to fix it — "receiver rewriting during expansion, or type parameters on ModuleInstanciation". I tried the first. It cannot work, and this records why so it is not rediscovered.

No behaviour change: a test and the backlog entry.

What was tried

Expansion copies the module body into the user and replaces the module's type parameters in type positions. A requirement of a bound is called on the parameter itself, T.show(x), and that receiver is a name resolved through lookupType, so the replacement never reaches it. Renaming it to the using class's parameter is what "receiver rewriting" meant.

The rename works. With it, the error moves from the blanket rejection to Could not find variable K at the dispatch — which is not a mistake in the rename but this, in NameResolution.nextScope:

if (currentScope instanceof ModuleInstanciation) {
return nextScope(moduleInstanciation.attrModuleOrigin());
}

A module body resolves in the module's own scope, not in the scope of whoever uses it, so it cannot capture the user's names. That is deliberate, and it means a receiver renamed to the using class's parameter names something the scope cannot see. The approach is ruled out rather than incomplete.

Reverted, so the tree is unchanged.

What is left

The other half of the original note: type parameters on ModuleInstanciation. The instantiation declares the parameter itself, bound to the argument, so the copied body keeps saying T and T resolves with no rename. That needs ModuleInstanciation to carry type parameters in wurstscript.parseq and everything reading that node to follow — a grammar change rather than a patch to the expander, which is why I stopped instead of half-building it.

A second case survives even then: use Shower<int>. A requirement is dispatched on a type parameter, so int.show(x) is not a dispatch at all; a concrete argument has to resolve to the instance during expansion rather than by name.

The test

boundOnModuleTypeParameterIsRejected pins the rejection, in the same shape as dispatchInsideConstructorIsRejectedForLua, and its comment carries the scope rule above. Should the grammar change land, this test fails and becomes the success case.

Full suite green.

The item proposed receiver rewriting during expansion or type parameters on
ModuleInstanciation. The first was tried and does not work, for a reason worth
keeping rather than rediscovering.
Expansion replaces the module's type parameters in type positions. A
requirement is called on the parameter itself, T.show(x), and that receiver is
a name rather than a type, so the replacement never reaches it. Renaming it to
the using class's parameter is what the note meant, and the rename works - with
it the error moves from the rejection to "Could not find variable K" at the
dispatch. That is NameResolution.nextScope sending a ModuleInstanciation to
attrModuleOrigin() instead of to the class using it, so a module body cannot
see the names of whoever uses it. Deliberate, and it rules the approach out.
What is left is the other half: the instantiation declaring the parameter
itself, so the body keeps saying T and T resolves. That is a grammar change.
The test is kept as a pinned rejection carrying that reason, and an argument
which is a concrete type is recorded as a second case, since a requirement
dispatches on a type parameter and int.show(x) is not a dispatch at all.
Sign up for freeto 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.

1 participant

@Frotty