Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve module method checks #283

Open
wants to merge 4 commits into
base: hkmc2
Choose a base branch
from

Conversation

FlandiaYingman
Copy link

@FlandiaYingman FlandiaYingman commented Feb 19, 2025

This PR improves the module method checks introduced in #233, and makes preparation in order to move all the checks to the resolution stage. It also fixes:

the problem that "module" parameters were not treated as modules

:e
fun f8(module m: M) = m

the problem that self referencing modules were not treated as modules

module Foo with
  val self = this

the problem that term definitions returning a module were not treated as modules

module A with
  fun f() = 2

module B with
  val a: module A = A

print(B.a) // B.a should be a module, and should be rejected passing to a regular parameter

Comment on lines 1118 to 1120
case sym: TermSymbol =>
// TODO: How can a TermSymbol accept a Declaration
// sym.defn = S(TermDefinition(ctx.outer, Fun, sym, Nil, Nil, sign, N, FlowSymbol(s"‹result of ${sym}›"), TermDefFlags.empty, Nil))
Copy link
Author

Choose a reason for hiding this comment

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

I was a little confused about TermSymbol: it seems like the symbol becomes TermSymbol if the scope is in the class (i.e., a class's parameter), but other parts of code seem to directly create a BlockMemberSymbol to represent a term. Also, I found some code seems to related to it, but it was commented with some reason 🤔

https://github.com/FlandiaYingman/mlscript/blob/bb1da72d39e09bca135c590ed19a73c36a99efee/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala#L43-L48

So I was not very sure how to deal with this here. I was thinking to change the symbol to BlockMemberSymbol instead but I don't know if that's appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right. This part of the module system is not fully/adequately worked out yet.

A couple of things influencing the current WIP design were:

  • class parameters act both as normal parameters and as fields, complicating the handling of parameters
  • let bindings in classes are basically private fields

But actually I realized class parameters need a very different handling than that. Consider this:

// FIXME: in super calls, class params should actually be viewed as a `VarSymbol`, not a `TermSymbol` wit owner
:sjs
class Baz(z) extends Bar(z * 1) with
fun baz = this.bar * 2
//│ JS (unsanitized):
//│ let Baz1;
//│ Baz1 = function Baz(z1) { return new Baz.class(z1); };
//│ Baz1.class = class Baz extends Bar3.class {
//│ constructor(z) {
//│ let tmp2;
//│ tmp2 = this.z * 1;
//│ super(tmp2);
//│ this.z = z;
//│ }
//│ get baz() {
//│ return this.bar * 2;
//│ }
//│ toString() { return "Baz(" + globalThis.Predef.render(this.z) + ")"; }
//│ };

Here, the current scheme always treats the class parameter z symbol as a field so it always code-gens z as this.z; but this is not valid in the pre-constructor (i.e., before super is called). So it seems to me we should just treat all parameters the same, including class parameters, but classes would additionally get some derived symbol form that represents a field induced by a class parameter.

Copy link
Contributor

@LPTK LPTK Feb 19, 2025

Choose a reason for hiding this comment

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

I think the obvious thing to do is simply to desugar each class parameter z into a normal parameter z (VarSymbol) along with a val, in the class body, that binds a field named z to that parameter, using a TermDefinition.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then it would have a proper BlockMemberSymbol.

Copy link
Contributor

@LPTK LPTK Feb 19, 2025

Choose a reason for hiding this comment

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

ie desugar elaborate

class Foo(val z) with
  blah

into

class Foo(z‹123) with
  val member:z124= z123›
  blah

@LPTK LPTK marked this pull request as draft February 19, 2025 06:47
@FlandiaYingman FlandiaYingman changed the title [WIP] Improve module method checks Improve module method checks Feb 19, 2025
@FlandiaYingman FlandiaYingman marked this pull request as ready for review February 19, 2025 06:52
@FlandiaYingman FlandiaYingman force-pushed the move-module-method-checks branch from bb1da72 to ce9abc4 Compare February 23, 2025 09:04
@FlandiaYingman
Copy link
Author

class Foo(val z) with
  blah

should now be elaborated into

class Foo(z‹123) with
  val member:z124= z123›
  blah

I didn't differentiate class Foo(val z) and class Foo(z) (they all elaborated to an additional val binding member:z) because all current tests seem to use the latter. But I think this behavior can be easily modified if we want.

@@ -25,19 +25,27 @@ class Foo[A] with



// Note: the elaborator elaborates class parameters into additional field definitions;
// this is why the following code yields two same errors.
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed in the meeting, let's not duplicate the annotation here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't forget to remove this FIXME comment!

// FIXME: in super calls, class params should actually be viewed as a `VarSymbol`, not a `TermSymbol` wit owner

@@ -117,6 +117,7 @@ class InstSymbol(val origin: Symbol)(using State) extends LocalSymbol:

class VarSymbol(val id: Ident)(using State) extends BlockLocalSymbol(id.name) with NamedSymbol with LocalSymbol:
val name: Str = id.name
override def toLoc: Option[Loc] = id.toLoc
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
override def toLoc: Option[Loc] = id.toLoc
override def toLoc: Opt[Loc] = id.toLoc

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.

2 participants