Skip to content

Reject abstract members in non-abstract classes#1647

Merged
stackoverflow merged 2 commits into
apple:mainfrom
vinayakj592:fix-abstract-member-in-non-abstract-class
Jun 10, 2026
Merged

Reject abstract members in non-abstract classes#1647
stackoverflow merged 2 commits into
apple:mainfrom
vinayakj592:fix-abstract-member-in-non-abstract-class

Conversation

@vinayakj592

Copy link
Copy Markdown
Contributor

Fixes #1614.

Context

A non-abstract class (or module) was allowed to declare abstract properties and methods.
Because such an enclosing type is instantiable, an abstract member there can never be guaranteed
an implementation — so the contradiction surfaced only as a runtime error when the member was
accessed (Cannot invoke abstract method), or not at all.

This makes it a compile-time error to declare an abstract member unless its enclosing class or
module is also abstract. This is consistent with how Pkl already rejects instantiating an abstract
class, and mirrors how Java and Kotlin treat abstract members.

Before

class Foo {
  abstract bar: Int
}
res = new Foo { bar = 5 }   // evaluated successfully (should fail)
class Foo {
  abstract function bar(): Int
}
res = new Foo {}            // evaluated successfully; res.bar() failed only at runtime

After

–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.

2 | abstract bar: Int
    ^^^^^^^^
at Foo

A member can only be `abstract` if its enclosing class is also `abstract`.

Implementation

  • AstBuilder now validates, while building the AST, that a non-abstract class/module declares no
    abstract members. The check runs in both visitClass and visitModule, and the error points at
    the abstract keyword.
  • Adds the abstractMemberInNonAbstractClass error message.

Scope: classes and modules

The issue describes classes; I applied the same rule to modules as well, since a module is a class
in Pkl and a non-abstract module is likewise directly evaluatable. Happy to narrow this to classes
only if you'd prefer — it's a one-line change either way.

The moduleMethodModifiers pkl-doc test fixture declared an abstract method at non-abstract module
level (relying on the old behavior); it's updated to an abstract module, and its expected
documentation output is regenerated.

Tests

  • New LanguageSnippetTests error cases: abstract property in a class, abstract method in a class,
    and abstract member in a module.
  • ./gradlew build passes (pkl-core and pkl-doc included).

A non-abstract class or module previously accepted `abstract`
property and method declarations. Because such an enclosing type is
instantiable, the abstract member can never be guaranteed an
implementation, so the contradiction surfaced only as a runtime
error when the member was accessed, if at all.

Validate this while building the AST: declaring an `abstract` member
is now an error unless its enclosing class or module is also
`abstract`, with the message pointing at the `abstract` keyword. This
matches the existing behavior for abstract classes and mirrors how
Java and Kotlin treat abstract members.

The `moduleMethodModifiers` pkl-doc test fixture relied on the old
behavior; make it an `abstract module` and regenerate its expected
documentation output.

Fixes apple#1614

@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.

Thanks! I think this solution makes sense. Another approach here could have been to defer the abstract check when the member is assigned to or accessed, but this feels more principled and catches obvious bugs earlier.

Note that this doesn't fully address #1614; this still works:

abstract class Foo {
  abstract bar: Int
}

class Qux extends Foo {}

qux: Qux = new {
  bar = 5
}

Waiting for eyes from @HT154 and @stackoverflow before merging this.

Comment thread docs/modules/release-notes/pages/changelog.adoc Outdated

@HT154 HT154 left a comment

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.

LGTM. Like @bioball said, this narrowly addresses #1614 but doesn't cover the related case from #1262, but that's fine to address as follow-up.

Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
@vinayakj592

Copy link
Copy Markdown
Contributor Author

Could you help me understand this? I understand your point that it doesn’t cover issue #1262, but I’m not clear on why it also doesn’t fully address issue #1614. Could you explain which parts of issue #1614 remain unresolved?
@HT154 @bioball

@bioball

bioball commented Jun 8, 2026

Copy link
Copy Markdown
Member

@vinayakj592 Class Qux still has an abstract member, because it did not override bar

@vinayakj592

Copy link
Copy Markdown
Contributor Author

@vinayakj592 Class Qux still has an abstract member, because it did not override bar

@bioball I see what you're saying, I will cover that while working on #1262 , I think this can be merged irrespective of that.

@bioball

bioball commented Jun 8, 2026

Copy link
Copy Markdown
Member

Yes, agree!

@vinayakj592

Copy link
Copy Markdown
Contributor Author

By the way, what does the typical review and merge cycle look like here? How long does it usually take for a PR to get reviewed and merged?

I’m planning to start contributing more actively, so I was also wondering if there are any discussion channels or community spaces apart from GitHub Discussions where contributors collaborate and ask questions.

@bioball

@bioball

bioball commented Jun 8, 2026

Copy link
Copy Markdown
Member

Feel free to join the Pkl community discord (see https://pkl.community/); us maintainers also hang out on there!

This PR is blocked on a review from @stackoverflow.

@vinayakj592

Copy link
Copy Markdown
Contributor Author

Thanks @bioball

@stackoverflow stackoverflow left a comment

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.

Look good. Thanks for the contribution.

@stackoverflow
stackoverflow merged commit ce03838 into apple:main Jun 10, 2026
22 checks passed
@bioball

bioball commented Jun 22, 2026

Copy link
Copy Markdown
Member

@vinayakj592 FYI: I explored a solution for #1262 in #1690. I think there's a couple interesting open questions to cover, though (see that PR's description)

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.

Non-abstract class can have abstract members

5 participants