diff --git a/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java b/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java index 6181cbde..a03d1476 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java @@ -72,7 +72,7 @@ public final class VmModifier { public static final int VALID_PROPERTY_MODIFIERS = ABSTRACT | LOCAL | HIDDEN | EXTERNAL | FIXED | CONST; - public static final int VALID_OBJECT_MEMBER_MODIFIERS = LOCAL; + public static final int VALID_OBJECT_MEMBER_MODIFIERS = LOCAL | CONST; public static final int TYPEALIAS_OBJECT_MEMBER = TYPE_ALIAS | CONST; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java index ac13402a..7c28b55b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java @@ -721,12 +721,22 @@ public final class AstBuilder extends AbstractAstBuilder { @Nullable TypeAnnotationContext typeAnnCtx, @Nullable ExprContext exprCtx, @Nullable List bodyCtx) { - + var modifiers = + doVisitModifiers( + modifierCtxs, VmModifier.VALID_OBJECT_MEMBER_MODIFIERS, "invalidObjectMemberModifier"); + if (VmModifier.isConst(modifiers) && !VmModifier.isLocal(modifiers)) { + @SuppressWarnings("OptionalGetWithoutIsPresent") + var constModifierCtx = + modifierCtxs.stream().filter((it) -> it.CONST() != null).findFirst().get(); + throw exceptionBuilder() + .evalError("invalidConstObjectMemberModifier") + .withSourceSection(createSourceSection(constModifierCtx)) + .build(); + } return doVisitObjectProperty( createSourceSection(ctx), createSourceSection(propertyName), - doVisitModifiers( - modifierCtxs, VmModifier.VALID_OBJECT_MEMBER_MODIFIERS, "invalidObjectMemberModifier"), + modifiers, propertyName.getText(), typeAnnCtx, exprCtx, diff --git a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties index 98b7ea1b..debe015b 100644 --- a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties +++ b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties @@ -19,6 +19,9 @@ Modifier `{0}` is not applicable to properties. invalidObjectMemberModifier=\ Modifier `{0}` is not applicable to object members. +invalidConstObjectMemberModifier=\ +Modifier `const` can only be applied to object members that are also `local`. + objectMethodMustBeLocal=\ Method needs a `local` modifier because it is defined in an object, not a class. diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/modules/Birds2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/modules/Birds2.pkl new file mode 100644 index 00000000..598f5a83 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/modules/Birds2.pkl @@ -0,0 +1,5 @@ +module Birds2 + +abstract class Bird + +bird: Bird diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/constModifier4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/constModifier4.pkl new file mode 100644 index 00000000..c378406b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/constModifier4.pkl @@ -0,0 +1,15 @@ +const baz = 15 + +foo { + const local bar = baz + + const local bar2 = biz() + + const local function biz() = baz + + qux = bar + + corge = biz() + + corge2 = bar2 +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalAmendModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalAmendModule.pkl new file mode 100644 index 00000000..40baed68 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalAmendModule.pkl @@ -0,0 +1,9 @@ +amends ".../input-helper/modules/Birds2.pkl" + +const local myBirdName = "Birdy" + +local class MyBird extends Bird { + name: String = myBirdName +} + +bird = new MyBird {} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalMethod.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalMethod.pkl new file mode 100644 index 00000000..6bbcdcaa --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalMethod.pkl @@ -0,0 +1,7 @@ +bar = 15 + +obj { + const local function foo() = bar + + res = foo() +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalProperty.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalProperty.pkl new file mode 100644 index 00000000..9ca82c0b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/const/constLocalProperty.pkl @@ -0,0 +1,5 @@ +foo { + res1 = 15 + const local qux = res1 + res2 = qux +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/constModifier4.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/constModifier4.pcf new file mode 100644 index 00000000..1968d7c7 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/constModifier4.pcf @@ -0,0 +1,6 @@ +baz = 15 +foo { + qux = 15 + corge = 15 + corge2 = 15 +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalAmendModule.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalAmendModule.pcf new file mode 100644 index 00000000..9617c291 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalAmendModule.pcf @@ -0,0 +1,3 @@ +bird { + name = "Birdy" +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalMethod.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalMethod.err new file mode 100644 index 00000000..ff56865a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalMethod.err @@ -0,0 +1,18 @@ +–– Pkl Error –– +Cannot reference property `bar` from here because it is not `const`. + +x | const local function foo() = bar + ^^^ +at constLocalMethod#obj.foo (file:///$snippetsDir/input/errors/const/constLocalMethod.pkl) + +To fix, do either of: +1. Add modifier `const` to property `bar` +2. Self-import this module, and reference this property from the import. + +x | res = foo() + ^^^^^ +at constLocalMethod#obj.res (file:///$snippetsDir/input/errors/const/constLocalMethod.pkl) + +xxx | text = renderer.renderDocument(value) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalProperty.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalProperty.err new file mode 100644 index 00000000..9043e878 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constLocalProperty.err @@ -0,0 +1,18 @@ +–– Pkl Error –– +Cannot reference property `res1` from here because it is not `const`. + +x | const local qux = res1 + ^^^^ +at constLocalProperty#foo.qux (file:///$snippetsDir/input/errors/const/constLocalProperty.pkl) + +To fix, do either of: +1. Add modifier `const` to property `res1` +2. Self-import this module, and reference this property from the import. + +x | res2 = qux + ^^^ +at constLocalProperty#foo.res2 (file:///$snippetsDir/input/errors/const/constLocalProperty.pkl) + +xxx | text = renderer.renderDocument(value) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constObjectMember.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constObjectMember.err index 9c50f992..7aec3f77 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constObjectMember.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/const/constObjectMember.err @@ -1,5 +1,5 @@ –– Pkl Error –– -Modifier `const` is not applicable to object members. +Modifier `const` can only be applied to object members that are also `local`. x | const foo = 10 ^^^^^