Require references from typealiases to be const (#516)

This adds a language change that requires references from typealiases
to the enclosing module to be `const`.

This is required because typealiases are not late-bound.

Rationale is laid out in SPICE-0007.

Also:
* Update documentation to reflect new rules.
* Fix `Project.pkl`; mark method `const` to not break said rule.
This commit is contained in:
Daniel Chao
2024-06-10 09:01:05 -07:00
committed by GitHub
parent 9cc9816440
commit a4c0a271b4
5 changed files with 37 additions and 7 deletions
@@ -1147,9 +1147,13 @@ Therefore, the same rules that apply to `fixed` also apply to `const`:
* The const-ness of a property or method must be preserved when it is overridden by a child class. * The const-ness of a property or method must be preserved when it is overridden by a child class.
[[class-and-annotation-const]] [[class-and-annotation-const]]
*Class and Annotation Scoping* *Class, Annotation, and Typealias Scoping*
Within a class or annotation body, any reference to a property or method of its enclosing module requires that the referenced member is `const`. In these following scenarios, any reference to a property or method of its enclosing module requires that the referenced member is `const`:
* Class body
* Annotation body
* Typealiased constrained type
.invalid2.pkl .invalid2.pkl
[source%parsed,{pkl}] [source%parsed,{pkl}]
@@ -1162,15 +1166,18 @@ class Bird {
@Deprecated { message = "Replace with \(pigeonName)" } // <2> @Deprecated { message = "Replace with \(pigeonName)" } // <2>
oldPigeonName: String oldPigeonName: String
typealias IsPigeonName = String(pigeonName) // <3>
---- ----
<1> Error: cannot reference non-const property `pigeonName` from a class. <1> Error: cannot reference non-const property `pigeonName` from a class.
<2> Error: cannot reference non-const property `pigeonName` from an annotation. <2> Error: cannot reference non-const property `pigeonName` from an annotation.
<3> Error: cannot reference non-const property `pigeonname` from a typealias.
This rule exists because classes and annotations are not <<late-binding,late bound>>; This rule exists because classes, annotations, and typealiases are not <<late-binding,late bound>>;
it is not possible to change the definition of a class or annotation by amending the module it is not possible to change the definition of these members by amending the module
where it is defined. where it is defined.
Generally, there are two strategies for referencing a property from a class or annotation: Generally, there are two strategies for referencing such properties:
*Add the `const` modifier to the referenced property* *Add the `const` modifier to the referenced property*
@@ -439,7 +439,7 @@ public final class SymbolTable {
String qualifiedName, String qualifiedName,
FrameDescriptor.Builder frameDescriptorBuilder, FrameDescriptor.Builder frameDescriptorBuilder,
List<TypeParameter> typeParameters) { List<TypeParameter> typeParameters) {
super(parent, name, qualifiedName, ConstLevel.NONE, frameDescriptorBuilder, typeParameters); super(parent, name, qualifiedName, ConstLevel.MODULE, frameDescriptorBuilder, typeParameters);
} }
} }
@@ -0,0 +1,5 @@
typealias MyValue = Any(isValid)
isValid = true
myValue: MyValue = 1
@@ -0,0 +1,18 @@
–– Pkl Error ––
Cannot reference property `isValid` from here because it is not `const`.
x | typealias MyValue = Any(isValid)
^^^^^^^
at constTypeAliasConstraint#myValue (file:///$snippetsDir/input/errors/const/constTypeAliasConstraint.pkl)
To fix, do either of:
1. Add modifier `const` to property `isValid`
2. Self-import this module, and reference this property from the import.
x | myValue: MyValue = 1
^
at constTypeAliasConstraint#myValue (file:///$snippetsDir/input/errors/const/constTypeAliasConstraint.pkl)
xxx | text = renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
+1 -1
View File
@@ -221,7 +221,7 @@ function newInstance(enclosingModule: Module): Project = new {
projectFileUri = reflect.Module(enclosingModule).uri projectFileUri = reflect.Module(enclosingModule).uri
} }
local hasVersion = (it: Uri) -> const local hasVersion = (it: Uri) ->
let (versionSep = it.lastIndexOf("@")) let (versionSep = it.lastIndexOf("@"))
if (versionSep == -1) false if (versionSep == -1) false
else let (version = it.drop(versionSep + 1)) else let (version = it.drop(versionSep + 1))