Skip to content

Add this as a self type#1708

Open
HT154 wants to merge 2 commits into
apple:mainfrom
HT154:this-type
Open

Add this as a self type#1708
HT154 wants to merge 2 commits into
apple:mainfrom
HT154:this-type

Conversation

@HT154

@HT154 HT154 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

This PR:

  • Adds type this, a fully featured self type for use in modules or classes.
    • Forbids use of this in typealias bodies.
  • Deprecates usage of the module type in class and typealias bodies with a warning, to be an error in a future release.
  • Adopts this in the standard library:
    • pkl.base#Any.getClass() now returns Class<this>.
    • pkl.base#Any.ifNonNull() now accepts an argument of type (this) -> Result.
    • pkl.ref#Domain.renderReference() now accepts a Reference<this, Any>.
  • This also a corner case with ReferenceTypeNode, module types, and type aliases that caused module types in the referent position to always be resolved to the module where the Reference type annotation is instead of where module is used.

For ease of review, this PR consists of two commits; the first is all implementation changes, the second includes regenerated pkl-doc test output.

Resolves #1612
Resolves #1712

Comment thread pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java Outdated
Comment on lines +190 to +194
/**
* If {@code frame} is provided then self types should be resolved to real types, otherwise return
* the self PType
*/
protected PType doExport(@Nullable VirtualFrame frame) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ReferenceTypeNode needs to export the real class here, so when a frame is passed NonFinalSelfTypeNode uses it to get the target's class instead of exporting PType.MODULE/PType.THIS.

@HT154
HT154 force-pushed the this-type branch 2 times, most recently from 41b3a9f to 9d20551 Compare July 3, 2026 06:18
@HT154
HT154 marked this pull request as ready for review July 4, 2026 19:10
@HT154
HT154 force-pushed the this-type branch 2 times, most recently from b00e394 to a5729f3 Compare July 4, 2026 19:12
@HT154
HT154 force-pushed the this-type branch 4 times, most recently from 835f94f to 6c21103 Compare July 15, 2026 19:18

@bioball bioball left a comment

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.

Left some comments!

The major ones are:

  • Much of the resolution logic should happen inside AstBuilder; we shouldn't need to traverse the truffle tree
  • Not sure about the realTypeAliasFrame logic added to type node execution; this feels like a workaround to a deeper issue
  • Not sure if TypeNode.export() should be deferencing self types; this feels like the responsibility of the caller, not TypeNode itself

if (typeAliasNode.isBodyChild(prevParent)) {
throw exceptionBuilder()
.evalError("invalidSelfTypeUsage", "this", "type alias")
.build();

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.

I think we should handle this error at parse time in AstBuilder, rather than here. We should walk the parser syntax tree, rather than Truffle

}
PType.THIS -> {
+"this"
}

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.

Need to also handle PType.THIS in SearchIndexGenerator and PackageDataGenerator

Comment thread pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java Outdated

return VmUtils.getClass(getReceiverNode.executeGeneric(frame));
}
}

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.

Same comment as above: I think this should be done at parse time (within AstBuilder), not here.

This truffle node can receive the (nullable) enclosing class name, and use a combination of GetModuleNode (in the case of no enclosing module) and ResolveSimpleDeclaredTypeNode (in the case of an enclosing module) to get the correct VmClass

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.

Within AstBuilder, we can walk up the symbol table scope until we find a Class scope to find the class identifier.

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 can also maybe introduce a ReadClassNode or something like that perhaps that receives the identifier name, whether it's local, and whether it's the module class.


private VirtualFrame getEffectiveFrame(VirtualFrame frame) {
var realFrame = VmLanguage.get(this).localContext.get().getRealTypeAliasFrame();
return realFrame != null ? realFrame : frame;

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.

I'm trying to understand what this code is doing; is this to address the case of typealias Foo = module?'

If so, I think probably a better solution here is: when we execute a TypeAliasNode, we can replace the module type when we execute the type alias itself (e.g. inside TypeAliasNode#executeGeneric)

// gets replaced upon instantiation, and Function can bubble up to users as a reference error
// if accessed.
result.add(type);
// PType.MODULE and PType.THIS can never be encountered here; caller must deref self types

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.

I don't think this is actually happening here? This class is just calling clazz.export() in the constructor, which won't pass in a frame, which won't defer self types.

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.

Also: I don't know if it's worth investing too much into this code path; I think we should introduce a VmType value. If we had that, the TypeNode.export() call wouldn't be used.

}
};

/** The type of the enclosing owner. */

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.

Suggested change
/** The type of the enclosing owner. */
/** The {@code this} type. */

`Reference` referent type argument may not include type constraints.

invalidSelfTypeUsage=\
`{0}` type annotations are not allowed in {1} bodies.

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.

A "type annotation" is the thing on the right-hand side of a typed identifier; foo: Bar has type annotation : Bar

Suggested change
`{0}` type annotations are not allowed in {1} bodies.
`{0}` types are not allowed in {1} bodies.

}

@Override
@TruffleBoundary

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.

Leftover code? This didn't need a truffle boundary before, so I wouldn't expect this to need one now

@@ -0,0 +1,14 @@
–– Pkl Error ––
`this` type annotations are not allowed in type alias bodies.

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.

For consistency with other error messages, I think let's phrase this as:

Cannot declare `this` types inside type alias bodies.

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.

module types as typelias type args are evaluated in the context of the alias's module, not where module is used Pkl is missing a self-type

2 participants