Remove abstract properties (#1781)

This commit is contained in:
Jen Basch
2026-07-29 15:05:19 -07:00
committed by GitHub
parent f6107b6e86
commit 50a09f59dc
24 changed files with 80 additions and 179 deletions
@@ -71,6 +71,7 @@ public final class VmModifier {
public static final int VALID_METHOD_MODIFIERS = ABSTRACT | LOCAL | EXTERNAL | CONST;
// for compat, properties may be parsed with abstract modifier but this is ignored
public static final int VALID_PROPERTY_MODIFIERS =
ABSTRACT | LOCAL | HIDDEN | EXTERNAL | FIXED | CONST;
@@ -1476,7 +1476,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var scope = (ModuleScope) symbolTable.getCurrentScope();
scope.setModifiers(modifiers);
checkAbstractMembersAllowed(modifiers, mod.getProperties(), mod.getMethods());
checkAbstractMethodsAllowed(modifiers, mod.getMethods(), "module");
// visit imports first so that we already have the object member name available
var imports = mod.getImports();
@@ -1722,7 +1722,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
List<ClassProperty> properties = bodyNode != null ? bodyNode.getProperties() : List.of();
List<ClassMethod> methods = bodyNode != null ? bodyNode.getMethods() : List.of();
registerClassScopeNames(scope, properties, methods);
checkAbstractMembersAllowed(modifiers, properties, methods);
checkAbstractMethodsAllowed(modifiers, methods, "class");
var supertypeCtx = clazz.getSuperClass();
@@ -1813,26 +1813,19 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
};
}
private void checkAbstractMembersAllowed(
int enclosingModifiers, List<ClassProperty> properties, List<ClassMethod> methods) {
private void checkAbstractMethodsAllowed(
int enclosingModifiers, List<ClassMethod> methods, String context) {
if (VmModifier.isAbstract(enclosingModifiers)) {
return;
}
for (var property : properties) {
checkMemberNotAbstract(property.getModifiers());
}
for (var method : methods) {
checkMemberNotAbstract(method.getModifiers());
}
}
private void checkMemberNotAbstract(List<Modifier> modifiers) {
for (var modifier : modifiers) {
if (modifier.getValue() == ModifierValue.ABSTRACT) {
throw exceptionBuilder()
.evalError("abstractMemberInNonAbstractClass")
.withSourceSection(createSourceSection(modifier.span()))
.build();
for (var modifier : method.getModifiers()) {
if (modifier.getValue() == ModifierValue.ABSTRACT) {
throw exceptionBuilder()
.evalError("abstractMethodInNonAbstractType", context)
.withSourceSection(createSourceSection(modifier.span()))
.build();
}
}
}
}
@@ -1878,9 +1871,11 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var headerEnd = typeAnnotation != null ? typeAnnotation.span() : name.span();
var headerSection = createSourceSection(headerStart.endWith(headerEnd));
var modifiers =
var fullModifiers =
doVisitModifiers(
modifierList, VmModifier.VALID_PROPERTY_MODIFIERS, "invalidPropertyModifier");
// for compat, properties may be abstract, but ignore this
var modifiers = fullModifiers & ~VmModifier.ABSTRACT;
var isLocal = VmModifier.isLocal(modifiers);
var propertyName = org.pkl.core.runtime.Identifier.property(name.getValue(), isLocal);
@@ -1899,12 +1894,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
.withSourceSection(headerSection)
.build();
}
if (VmModifier.isAbstract(modifiers)) {
throw exceptionBuilder()
.evalError("abstractMemberCannotHaveBody")
.withSourceSection(headerSection)
.build();
}
bodyNode = visitExpr(expr);
} else if (!objectBodies.isEmpty()) { // prop { ... }
if (typeAnnotation != null) {
@@ -1931,9 +1920,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
if (bodyNode instanceof LanguageAwareNode languageAwareNode) {
languageAwareNode.initLanguage(language);
}
} else if (VmModifier.isAbstract(modifiers)) {
bodyNode =
new CannotInvokeAbstractPropertyNode(headerSection, scope.getQualifiedName());
} else {
bodyNode = null; // will be given a default by UnresolvedPropertyNode
}
@@ -1,36 +0,0 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.ast.builder;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
public final class CannotInvokeAbstractPropertyNode extends ExpressionNode {
private final String propertyName;
public CannotInvokeAbstractPropertyNode(SourceSection section, String propertyName) {
super(section);
this.propertyName = propertyName;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().evalError("cannotInvokeAbstractProperty", propertyName).build();
}
}
@@ -180,8 +180,7 @@ public final class MirrorFactories {
.addProperty(
"defaultValue",
property ->
property.isAbstract()
|| property.isExternal()
property.isExternal()
|| property
.getInitializer()
.isUndefined(VmUtils.createEmptyMaterializedFrame())
@@ -43,9 +43,6 @@ Type alias definitions must not be cyclic.
cannotInvokeAbstractMethod=\
Cannot invoke abstract method `{0}`.
cannotInvokeAbstractProperty=\
Cannot invoke abstract property `{0}`.
cannotInvokeSupermethodFromHere=\
Cannot invoke a supermethod from here.
@@ -289,8 +286,8 @@ External members cannot have a body.
abstractMemberCannotHaveBody=\
Abstract members cannot have a body.
abstractMemberInNonAbstractClass=\
Cannot define an abstract member in a non-abstract class.\n\
abstractMethodInNonAbstractType=\
Cannot define an abstract method in a non-abstract {0}.\n\
\n\
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -1,5 +0,0 @@
class Foo {
abstract bar: Int
}
res = new Foo { bar = 5 }
@@ -1 +0,0 @@
abstract foo: Int
@@ -0,0 +1 @@
abstract function foo(): Int
@@ -18,7 +18,7 @@ res3 = f(List(1, 2))
res4 = f(Set(1, 2))
abstract class Animal {
abstract size: String
size: String
abstract function walk(): String
}
@@ -1,8 +0,0 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
x | abstract bar: Int
^^^^^^^^
at abstractMemberInNonAbstractClass#Foo (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractClass.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -1,8 +0,0 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
x | abstract foo: Int
^^^^^^^^
at abstractMemberInNonAbstractModule (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractModule.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -1,5 +1,5 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
Cannot define an abstract method in a non-abstract class.
x | abstract function bar(): Int
^^^^^^^^
@@ -0,0 +1,8 @@
–– Pkl Error ––
Cannot define an abstract method in a non-abstract module.
x | abstract function foo(): Int
^^^^^^^^
at abstractMethodInNonAbstractModule (file:///$snippetsDir/input/errors/abstractMethodInNonAbstractModule.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -3,7 +3,7 @@ module com.package1.classInheritance
abstract class MyClass1 {
/// Inherited property comment.
abstract property1: Boolean
property1: Boolean
/// function method1 in class MyClass1.
abstract function method1(arg: String): Boolean
@@ -8,9 +8,6 @@ abstract class Modifiers {
/// Property with `hidden` modifier.
hidden property2: Float = 3.14159265359
/// Property with `abstract` modifier.
abstract property3: Float
/// Property with multiple modifiers.
abstract hidden property4: Float
@@ -10,10 +10,4 @@ hidden property2: Float = 3.14159265359
/*
/// Property with `external` modifier.
external property3: Float
/// Property with `abstract` modifier.
abstract property4: Float
/// Property with multiple modifiers.
abstract external hidden property5: Float
*/
@@ -50,7 +50,7 @@
<div id="property1" class="anchor"> </div>
<div class="member"><a class="member-selflink material-icons" href="#property1">link</a>
<div class="member-left">
<div class="member-modifiers">abstract </div>
<div class="member-modifiers"></div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property1</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Boolean.html" class="name-ref">Boolean</a><a class="member-source-link" href="https://example.com/package1/classInheritance.pkl#L123-L456">Source</a></div>
@@ -58,23 +58,11 @@
</div>
</div>
</li>
<li>
<div id="property3" class="anchor"> </div>
<div class="member"><a class="member-selflink material-icons" href="#property3">link</a>
<div class="member-left">
<div class="member-modifiers">abstract </div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property3</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Float.html" class="name-ref">Float</a><a class="member-source-link" href="https://example.com/package1/classPropertyModifiers.pkl#L123-L456">Source</a></div>
<div class="doc-comment"><p>Property with <code>abstract</code> modifier.</p></div>
</div>
</div>
</li>
<li>
<div id="property4" class="anchor"> </div>
<div class="member hidden-member"><a class="member-selflink material-icons" href="#property4">link</a>
<div class="member-left">
<div class="member-modifiers">abstract hidden </div>
<div class="member-modifiers">hidden </div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property4</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Float.html" class="name-ref">Float</a><a class="member-source-link" href="https://example.com/package1/classPropertyModifiers.pkl#L123-L456">Source</a></div>
File diff suppressed because one or more lines are too long
@@ -50,7 +50,7 @@
<div id="property1" class="anchor"> </div>
<div class="member"><a class="member-selflink material-icons" href="#property1">link</a>
<div class="member-left">
<div class="member-modifiers">abstract </div>
<div class="member-modifiers"></div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property1</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Boolean.html" class="name-ref">Boolean</a><a class="member-source-link" href="https://example.com/package1/classInheritance.pkl#L123-L456">Source</a></div>
@@ -58,23 +58,11 @@
</div>
</div>
</li>
<li>
<div id="property3" class="anchor"> </div>
<div class="member"><a class="member-selflink material-icons" href="#property3">link</a>
<div class="member-left">
<div class="member-modifiers">abstract </div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property3</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Float.html" class="name-ref">Float</a><a class="member-source-link" href="https://example.com/package1/classPropertyModifiers.pkl#L123-L456">Source</a></div>
<div class="doc-comment"><p>Property with <code>abstract</code> modifier.</p></div>
</div>
</div>
</li>
<li>
<div id="property4" class="anchor"> </div>
<div class="member hidden-member"><a class="member-selflink material-icons" href="#property4">link</a>
<div class="member-left">
<div class="member-modifiers">abstract hidden </div>
<div class="member-modifiers">hidden </div>
</div>
<div class="member-main">
<div class="member-signature"><span class="name-decl">property4</span>: <a href="https://pages.github.com/apple/pkl/stdlib/pkl/0.24.0/base/Float.html" class="name-ref">Float</a><a class="member-source-link" href="https://example.com/package1/classPropertyModifiers.pkl#L123-L456">Source</a></div>
File diff suppressed because one or more lines are too long
+42 -42
View File
@@ -740,28 +740,28 @@ class Resource {
/// use [Number] instead of [Float] in type annotations.
abstract external class Number extends Any {
/// A [Duration] with value [this] and unit `"ns"` (nanoseconds).
abstract ns: Duration
ns: Duration
/// A [Duration] with value [this] and unit `"us"` (microseconds).
abstract us: Duration
us: Duration
/// A [Duration] with value [this] and unit `"ms"` (milliseconds).
abstract ms: Duration
ms: Duration
/// A [Duration] with value [this] and unit `"s"` (seconds).
abstract s: Duration
s: Duration
/// A [Duration] with value [this] and unit `"min"` (minutes).
abstract min: Duration
min: Duration
/// A [Duration] with value [this] and unit `"h"` (hours).
abstract h: Duration
h: Duration
/// A [Duration] with value [this] and unit `"d"` (days).
abstract d: Duration
d: Duration
/// A [DataSize] with value [this] and unit `"b"` (bytes).
abstract b: DataSize
b: DataSize
/// A [DataSize] with value [this] and unit `"kb"` (kilobytes).
///
@@ -769,7 +769,7 @@ abstract external class Number extends Any {
/// ```
/// 1.kb == 1000.b
/// ```
abstract kb: DataSize
kb: DataSize
/// A [DataSize] with value [this] and unit `"mb"` (megabytes).
///
@@ -777,7 +777,7 @@ abstract external class Number extends Any {
/// ```
/// 1.mb == 1000.kb
/// ```
abstract mb: DataSize
mb: DataSize
/// A [DataSize] with value [this] and unit `"gb"` (gigabytes).
///
@@ -785,7 +785,7 @@ abstract external class Number extends Any {
/// ```
/// 1.gb == 1000.mb
/// ```
abstract gb: DataSize
gb: DataSize
/// A [DataSize] with value [this] and unit `"tb"` (terabytes).
///
@@ -793,7 +793,7 @@ abstract external class Number extends Any {
/// ```
/// 1.tb == 1000.gb
/// ```
abstract tb: DataSize
tb: DataSize
/// A [DataSize] with value [this] and unit `"pb"` (petabytes).
///
@@ -801,7 +801,7 @@ abstract external class Number extends Any {
/// ```
/// 1.pb == 1000.tb
/// ```
abstract pb: DataSize
pb: DataSize
/// A [DataSize] with value [this] and unit `"kib"` (kibibytes).
///
@@ -809,7 +809,7 @@ abstract external class Number extends Any {
/// ```
/// 1.kib == 1024.b
/// ```
abstract kib: DataSize
kib: DataSize
/// A [DataSize] with value [this] and unit `"mib"` (mebibytes).
///
@@ -817,7 +817,7 @@ abstract external class Number extends Any {
/// ```
/// 1.mib == 1024.kib
/// ```
abstract mib: DataSize
mib: DataSize
/// A [DataSize] with value [this] and unit `"gib"` (gibibytes).
///
@@ -825,7 +825,7 @@ abstract external class Number extends Any {
/// ```
/// 1.gib == 1024.mib
/// ```
abstract gib: DataSize
gib: DataSize
/// A [DataSize] with value [this] and unit `"tib"` (tebibytes).
///
@@ -833,7 +833,7 @@ abstract external class Number extends Any {
/// ```
/// 1.tib == 1024.gib
/// ```
abstract tib: DataSize
tib: DataSize
/// A [DataSize] with value [this] and unit `"pib"` (pebibytes).
///
@@ -841,14 +841,14 @@ abstract external class Number extends Any {
/// ```
/// 1.pib == 1024.tib
/// ```
abstract pib: DataSize
pib: DataSize
/// The sign of this number.
///
/// Returns `0` for `0`, `0.0`, `-0.0`, and [NaN],
/// `1` for positive numbers (including [Infinity]),
/// and `-1` for negative numbers (including `-`[Infinity]).
abstract sign: Number
sign: Number
/// The absolute value of this number.
///
@@ -862,7 +862,7 @@ abstract external class Number extends Any {
/// (-Infinity).abs == Infinity
/// NaN.abs == NaN
/// ```
abstract abs: Number
abs: Number
/// Rounds this number to the next mathematical integer towards [Infinity].
///
@@ -870,7 +870,7 @@ abstract external class Number extends Any {
/// If [this] is [NaN], [Infinity], -[Infinity], `0.0`, or `-0.0`, returns [this].
/// Otherwise, returns the smallest [Float] that is greater than or equal to [this]
/// and is equal to a mathematical integer.
abstract ceil: Number
ceil: Number
/// Rounds this number to the next mathematical integer towards -[Infinity].
///
@@ -878,7 +878,7 @@ abstract external class Number extends Any {
/// If [this] is [NaN], [Infinity], -[Infinity], `0.0`, or `-0.0`, returns [this].
/// Otherwise, returns the largest [Float] that is less than or equal to [this]
/// and is equal to a mathematical integer.
abstract floor: Number
floor: Number
/// Rounds this number to the nearest mathematical integer, breaking ties in favor
/// of the even integer.
@@ -946,23 +946,23 @@ abstract external class Number extends Any {
/// !(-Infinity).isPositive
/// !NaN.isPositive
/// ```
abstract isPositive: Boolean
isPositive: Boolean
/// Tells if this number is neither [NaN] nor [isInfinite].
abstract isFinite: Boolean
isFinite: Boolean
/// Tells if this number is [Infinity] or -[Infinity].
abstract isInfinite: Boolean
isInfinite: Boolean
/// Tells if this number is [NaN].
///
/// Always use this method when testing for [NaN].
/// Note that `x == NaN` is *not* a correct way to test for [NaN] because `NaN != NaN` as per the
/// IEEE spec.
abstract isNaN: Boolean
isNaN: Boolean
/// Tells if this number is not 0.
abstract isNonZero: Boolean
isNonZero: Boolean
/// Tells if this number is greater than or equal to [start] and less than or equal to
/// [inclusiveEnd].
@@ -2419,7 +2419,7 @@ abstract external class Collection<out Element> extends Any {
/// List().length == 0
/// ```
@AlsoKnownAs { names { "size"; "count" } }
abstract length: Int
length: Int
/// Tells whether this collection is empty.
///
@@ -2428,7 +2428,7 @@ abstract external class Collection<out Element> extends Any {
/// !List(1, 2, 3).isEmpty
/// List().isEmpty
/// ```
abstract isEmpty: Boolean
isEmpty: Boolean
/// Tells whether this collection is not empty.
///
@@ -2438,7 +2438,7 @@ abstract external class Collection<out Element> extends Any {
/// !List().isNotEmpty
/// ```
@Since { version = "0.31.0" }
abstract isNotEmpty: Boolean
isNotEmpty: Boolean
/// The first element in this collection.
///
@@ -2450,11 +2450,11 @@ abstract external class Collection<out Element> extends Any {
/// import("pkl:test").catch(() -> List().first)
/// ```
@AlsoKnownAs { names { "head" } }
abstract first: Element
first: Element
/// Same as [first] but returns [null] if this collection is empty.
@AlsoKnownAs { names { "head" } }
abstract firstOrNull: Element?
firstOrNull: Element?
/// The tail of this collection.
///
@@ -2466,11 +2466,11 @@ abstract external class Collection<out Element> extends Any {
/// import("pkl:test").catch(() -> List().rest)
/// ```
@AlsoKnownAs { names { "tail" } }
abstract rest: Collection<Element>
rest: Collection<Element>
/// Same as [rest] but returns [null] if this collection is empty.
@AlsoKnownAs { names { "tail" } }
abstract restOrNull: Collection<Element>?
restOrNull: Collection<Element>?
/// The last element in this collection.
///
@@ -2481,10 +2481,10 @@ abstract external class Collection<out Element> extends Any {
/// List(1, 2, 3).last == 3
/// import("pkl:test").catch(() -> List().last)
/// ```
abstract last: Element
last: Element
/// Same as [last] but returns [null] if this collection is empty.
abstract lastOrNull: Element?
lastOrNull: Element?
/// The single element in this collection.
///
@@ -2496,10 +2496,10 @@ abstract external class Collection<out Element> extends Any {
/// throws(() -> List().single)
/// throws(() -> List(1, 2, 3).single)
/// ```
abstract single: Element
single: Element
/// Same as [single] but returns [null] if this collection is empty or has more than one element.
abstract singleOrNull: Element?
singleOrNull: Element?
/// Tests if [element] is contained in this collection.
///
@@ -2790,10 +2790,10 @@ abstract external class Collection<out Element> extends Any {
/// Shorthand for `minWith((a, b) -> a < b)`.
///
/// Throws if this collection is empty, or if any two elements cannot be compared with `<`.
abstract min: Element
min: Element
/// Same as [min] but returns [null] if this collection is empty.
abstract minOrNull: Element?
minOrNull: Element?
/// Returns the first element in this collection that is less than or equal to any other element
/// after applying [selector].
@@ -2824,10 +2824,10 @@ abstract external class Collection<out Element> extends Any {
/// Shorthand for `maxWith((a, b) -> a < b)`.
///
/// Throws if this collection is empty, or if any two elements cannot be compared with `<`.
abstract max: Element
max: Element
/// Same as [max] but returns [null] if this collection empty.
abstract maxOrNull: Element
maxOrNull: Element
/// Returns the first element in this collection that is greater than or equal to any other
/// element after applying [selector].
+2 -2
View File
@@ -216,7 +216,7 @@ external class Module extends Declaration {
/// A class or type alias declaration.
abstract external class TypeDeclaration extends Declaration {
/// The class or type alias reflected upon.
abstract hidden reflectee: base.Class | base.TypeAlias
hidden reflectee: base.Class | base.TypeAlias
/// The module enclosing this type declaration.
///
@@ -224,7 +224,7 @@ abstract external class TypeDeclaration extends Declaration {
hidden enclosingDeclaration: Module
/// The type parameters of this type declaration.
abstract typeParameters: List<TypeParameter>
typeParameters: List<TypeParameter>
}
/// A class declaration.