diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmClass.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmClass.java index 68614aa86..1cbc68c6c 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmClass.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmClass.java @@ -497,6 +497,7 @@ public EconomicMap getTypedToDynamicMembers() { if (__typedToDynamicMembers == null) { __typedToDynamicMembers = createDelegatingMembers( + false, (member) -> new UntypedObjectMemberNode( null, new FrameDescriptor(), member, new DelegateToExtraStorageObjNode())); @@ -512,6 +513,7 @@ public EconomicMap getDynamicToTypedMembers() { if (__dynamicToTypedMembers == null) { __dynamicToTypedMembers = createDelegatingMembers( + true, (member) -> TypeCheckedPropertyNodeGen.create( null, @@ -530,6 +532,7 @@ public EconomicMap getMapToTypedMembers() { if (__mapToTypedMembers == null) { __mapToTypedMembers = createDelegatingMembers( + true, (member) -> TypeCheckedPropertyNodeGen.create( null, @@ -542,7 +545,7 @@ public EconomicMap getMapToTypedMembers() { } private EconomicMap createDelegatingMembers( - Function memberNodeFactory) { + boolean isConversionToTyped, Function memberNodeFactory) { var result = EconomicMaps.create(); for (var cursor = getAllProperties().getEntries(); cursor.advance(); ) { var property = cursor.getValue(); @@ -550,6 +553,10 @@ private EconomicMap createDelegatingMembers( // Dynamic/Map->Typed conversion: Overall it seems more useful for the typed object // to inherit its prototype's value for the hidden property (e.g., Module.output). if (property.isHidden()) continue; + // Dynamic/Map->Typed conversion: a `fixed` or `const` property's value is fixed by its + // declaration, so it must not be overridden by whatever value happens to be in the source + // Dynamic/Map. + if (isConversionToTyped && property.isConstOrFixed()) continue; var name = cursor.getKey(); var member = diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/dynamic.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/dynamic.pkl index 097dca131..2ab0e22cd 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/dynamic.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/dynamic.pkl @@ -57,6 +57,8 @@ examples { module.catch(() -> new Dynamic { name = "Pigeon" }.toTyped(Person).age) module.catch(() -> obj.toTyped(Pair)) // Pair is not a Typed module.catch(() -> obj.toTyped(ValueRenderer)) // ValueRenderer is abstract + (new Dynamic { name = "Pigeon"; id = 99 }).toTyped(FixedPerson) + (new Dynamic { name = "Pigeon"; id = 99 }).toTyped(ConstPerson) } } @@ -89,3 +91,13 @@ local class Person { name: String = "Default" age: UInt } + +local class FixedPerson { + name: String = "Default" + fixed id: Int = 1 +} + +local class ConstPerson { + name: String = "Default" + const id: Int = 1 +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/map.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/map.pkl index 4a178cb1a..bc375fe14 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/map.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/map.pkl @@ -137,8 +137,12 @@ examples { module.catch(() -> Map("name", "Pigeon").toTyped(Person).age) module.catch(() -> Map().toTyped(Int)) module.catch(() -> Map().toTyped(Abstract)) + Map("name", "Pigeon", "id", 99).toTyped(FixedPerson) + Map("name", "Pigeon", "id", 99).toTyped(ConstPerson) } } local class Person { name: String = "Default"; age: Int } local abstract class Abstract +local class FixedPerson { name: String = "Default"; fixed id: Int = 1 } +local class ConstPerson { name: String = "Default"; const id: Int = 1 } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/dynamic.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/dynamic.pcf index adc537afb..6e4627be4 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/dynamic.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/dynamic.pcf @@ -57,5 +57,13 @@ examples { "Tried to read property `age` but its value is undefined." "Class `Pair` is not a subtype of `Typed`." "Cannot instantiate abstract class `ValueRenderer`." + new { + name = "Pigeon" + id = 1 + } + new { + name = "Pigeon" + id = 1 + } } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/map.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/map.pcf index 785937863..4fd901c1b 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/map.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/map.pcf @@ -129,5 +129,13 @@ examples { "Tried to read property `age` but its value is undefined." "Class `Int` is not a subtype of `Typed`." "Cannot instantiate abstract class `map#Abstract`." + new { + name = "Pigeon" + id = 1 + } + new { + name = "Pigeon" + id = 1 + } } }