From 8f83885c758835e25658960fd4ebe5bb5da08f39 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Mon, 5 Jan 2026 11:39:24 -0800 Subject: [PATCH] Fix default value for non-final `module` type (#1392) This fixes an issue where the `module` type produces the wrong default value. Closes #1391 --- .../java/org/pkl/core/ast/MemberNode.java | 8 +- .../member/InferParentWithinMethodNode.java | 4 +- .../InferParentWithinObjectMethodNode.java | 4 +- .../member/InferParentWithinPropertyNode.java | 27 +- .../ast/member/DefaultPropertyBodyNode.java | 8 +- .../ast/member/LocalTypedPropertyNode.java | 6 +- .../org/pkl/core/ast/member/ObjectMember.java | 11 +- .../pkl/core/ast/member/PropertyTypeNode.java | 6 +- .../core/ast/type/GetParentForTypeNode.java | 17 +- .../java/org/pkl/core/ast/type/TypeNode.java | 234 ++++++++++++------ .../org/pkl/core/runtime/MirrorFactories.java | 7 +- .../java/org/pkl/core/runtime/VmUtils.java | 7 +- .../input/types/moduleType2.pkl | 20 ++ .../output/types/moduleType2.pcf | 7 + 14 files changed, 245 insertions(+), 121 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/moduleType2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/moduleType2.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java index 25b2e79f..d3c0c97a 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -16,6 +16,7 @@ package org.pkl.core.ast; import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.member.DefaultPropertyBodyNode; import org.pkl.core.runtime.VmExceptionBuilder; @@ -46,7 +47,8 @@ public abstract class MemberNode extends PklRootNode { return new VmExceptionBuilder().withSourceSection(getHeaderSection()); } - public boolean isUndefined() { - return bodyNode instanceof DefaultPropertyBodyNode propBodyNode && propBodyNode.isUndefined(); + public boolean isUndefined(VirtualFrame frame) { + return bodyNode instanceof DefaultPropertyBodyNode propBodyNode + && propBodyNode.isUndefined(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodNode.java index 5d381bd9..af9f5505 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -67,7 +67,7 @@ public final class InferParentWithinMethodNode extends ExpressionNode { var returnTypeDefaultValue = returnTypeNode.createDefaultValue( - language, method.getHeaderSection(), method.getQualifiedName()); + frame, language, method.getHeaderSection(), method.getQualifiedName()); if (returnTypeDefaultValue != null) { inferredParent = returnTypeDefaultValue; ownerNode = null; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinObjectMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinObjectMethodNode.java index f3a5a68a..fe18aa5e 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinObjectMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinObjectMethodNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -75,7 +75,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode { Object defaultReturnTypeValue = returnTypeNode.createDefaultValue( - language, member.getHeaderSection(), member.getQualifiedName()); + frame, language, member.getHeaderSection(), member.getQualifiedName()); if (defaultReturnTypeValue != null) { inferredParent = defaultReturnTypeValue; ownerNode = null; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinPropertyNode.java index 18e7b0c1..db630a05 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinPropertyNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -18,6 +18,7 @@ package org.pkl.core.ast.expression.member; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.runtime.*; @@ -35,9 +36,9 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { } @Specialization(guards = "!owner.isPrototype()") - protected Object evalTypedObject(VmTyped owner) { + protected Object evalTypedObject(VirtualFrame frame, VmTyped owner) { if (isLocalProperty) { - return getLocalPropertyDefaultValue(owner); + return getLocalPropertyDefaultValue(frame, owner); } try { @@ -51,7 +52,7 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { } @Specialization(guards = "owner.isPrototype()") - protected Object evalPrototype(VmTyped owner) { + protected Object evalPrototype(VirtualFrame frame, VmTyped owner) { var property = isLocalProperty ? @@ -66,7 +67,7 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { var typeNode = property.getTypeNode(); if (typeNode == null || typeNode.isUnknownType()) return VmDynamic.empty(); - var result = typeNode.getDefaultValue(); + var result = typeNode.getDefaultValue(frame); if (result != null) return result; // no default exists for this property type @@ -76,18 +77,18 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { } @Specialization - protected Object eval(@SuppressWarnings("unused") VmDynamic owner) { + protected Object eval(VirtualFrame frame, @SuppressWarnings("unused") VmDynamic owner) { if (isLocalProperty) { - return getLocalPropertyDefaultValue(owner); + return getLocalPropertyDefaultValue(frame, owner); } return VmDynamic.empty(); } @Specialization - protected Object eval(@SuppressWarnings("unused") VmListing owner) { + protected Object eval(VirtualFrame frame, @SuppressWarnings("unused") VmListing owner) { if (isLocalProperty) { - return getLocalPropertyDefaultValue(owner); + return getLocalPropertyDefaultValue(frame, owner); } assert ownPropertyName == Identifier.DEFAULT; @@ -96,9 +97,9 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { } @Specialization - protected Object eval(@SuppressWarnings("unused") VmMapping owner) { + protected Object eval(VirtualFrame frame, @SuppressWarnings("unused") VmMapping owner) { if (isLocalProperty) { - return getLocalPropertyDefaultValue(owner); + return getLocalPropertyDefaultValue(frame, owner); } assert ownPropertyName == Identifier.DEFAULT; @@ -106,13 +107,13 @@ public abstract class InferParentWithinPropertyNode extends ExpressionNode { return VmUtils.readMember(BaseModule.getMappingClass().getPrototype(), ownPropertyName); } - private Object getLocalPropertyDefaultValue(VmObjectLike owner) { + private Object getLocalPropertyDefaultValue(VirtualFrame frame, VmObjectLike owner) { assert isLocalProperty; var member = owner.getMember(ownPropertyName); assert member != null; - var defaultValue = member.getLocalPropertyDefaultValue(); + var defaultValue = member.getLocalPropertyDefaultValue(frame); if (defaultValue != null) return defaultValue; // no default exists for this property type diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/DefaultPropertyBodyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/DefaultPropertyBodyNode.java index 7e4a8da7..d840851d 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/DefaultPropertyBodyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/DefaultPropertyBodyNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -38,14 +38,14 @@ public final class DefaultPropertyBodyNode extends ExpressionNode { this.typeNode = typeNode; } - public boolean isUndefined() { - return typeNode == null || typeNode.getDefaultValue() == null; + public boolean isUndefined(VirtualFrame frame) { + return typeNode == null || typeNode.getDefaultValue(frame) == null; } @Override public Object executeGeneric(VirtualFrame frame) { if (typeNode != null) { - var defaultValue = typeNode.getDefaultValue(); + var defaultValue = typeNode.getDefaultValue(frame); if (defaultValue != null) return defaultValue; } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java index 7998bea8..b3c55f40 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -45,11 +45,11 @@ public final class LocalTypedPropertyNode extends RegularMemberNode { this.unresolvedTypeNode = unresolvedTypeNode; } - public @Nullable Object getDefaultValue() { + public @Nullable Object getDefaultValue(VirtualFrame frame) { if (!defaultValueInitialized) { defaultValue = typeNode.createDefaultValue( - language, member.getHeaderSection(), member.getQualifiedName()); + frame, language, member.getHeaderSection(), member.getQualifiedName()); defaultValueInitialized = true; } return defaultValue; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMember.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMember.java index 5d6d5a7f..83723b59 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMember.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMember.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -17,6 +17,7 @@ package org.pkl.core.ast.member; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ConstantNode; import org.pkl.core.ast.MemberNode; @@ -102,14 +103,14 @@ public final class ObjectMember extends Member { return callTarget; } - public boolean isUndefined() { - return getMemberNode() != null && getMemberNode().isUndefined(); + public boolean isUndefined(VirtualFrame frame) { + return getMemberNode() != null && getMemberNode().isUndefined(frame); } - public @Nullable Object getLocalPropertyDefaultValue() { + public @Nullable Object getLocalPropertyDefaultValue(VirtualFrame frame) { assert isProp() && isLocal(); return getMemberNode() instanceof LocalTypedPropertyNode propertyNode - ? propertyNode.getDefaultValue() + ? propertyNode.getDefaultValue(frame) : VmDynamic.empty(); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java index e3fb8f15..810f18f1 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -64,11 +64,11 @@ public final class PropertyTypeNode extends PklRootNode { return typeNode.execute(frame, frame.getArguments()[2]); } - public @Nullable Object getDefaultValue() { + public @Nullable Object getDefaultValue(VirtualFrame frame) { if (!defaultValueInitialized) { defaultValue = typeNode.createDefaultValue( - VmLanguage.get(this), getSourceSection(), qualifiedPropertyName); + frame, VmLanguage.get(this), getSourceSection(), qualifiedPropertyName); defaultValueInitialized = true; } return defaultValue; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/GetParentForTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/GetParentForTypeNode.java index 48f71834..d88581b8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/GetParentForTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/GetParentForTypeNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -26,6 +26,7 @@ import org.pkl.core.util.LateInit; /** Resolves `` to the type's default value in `new { ... }`. */ public final class GetParentForTypeNode extends ExpressionNode { @Child private UnresolvedTypeNode unresolvedTypeNode; + @Child private TypeNode typeNode; private final String qualifiedName; @CompilationFinal @LateInit Object defaultValue; @@ -37,14 +38,24 @@ public final class GetParentForTypeNode extends ExpressionNode { this.qualifiedName = qualifiedName; } + private TypeNode getTypeNode(VirtualFrame frame) { + if (typeNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + typeNode = unresolvedTypeNode.execute(frame); + adoptChildren(); + } + return typeNode; + } + @Override public Object executeGeneric(VirtualFrame frame) { if (defaultValue != null) return defaultValue; CompilerDirectives.transferToInterpreterAndInvalidate(); - var typeNode = unresolvedTypeNode.execute(frame); - defaultValue = typeNode.createDefaultValue(VmLanguage.get(this), sourceSection, qualifiedName); + var typeNode = getTypeNode(frame); + defaultValue = + typeNode.createDefaultValue(frame, VmLanguage.get(this), sourceSection, qualifiedName); if (defaultValue != null) { unresolvedTypeNode = null; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 023057b8..6e5906db 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -116,6 +116,7 @@ public abstract class TypeNode extends PklNode { // method arguments are used when default value contains a root node public @Nullable Object createDefaultValue( + VirtualFrame frame, VmLanguage language, // header section of the property or method that carries the type annotation SourceSection headerSection, @@ -473,6 +474,16 @@ public abstract class TypeNode extends PklNode { protected boolean acceptTypeNode(TypeNodeConsumer consumer) { return consumer.accept(this); } + + @Override + public @Nullable Object createDefaultValue( + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { + var moduleClass = ((VmTyped) getModuleNode.executeGeneric(frame)).getVmClass(); + return TypeNode.createDefaultValue(moduleClass); + } } public static final class StringLiteralTypeNode extends ObjectSlotTypeNode { @@ -504,7 +515,10 @@ public abstract class TypeNode extends PklNode { @Override public Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return literal; } @@ -572,7 +586,10 @@ public abstract class TypeNode extends PklNode { @Override public Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmDynamic.empty(); } @@ -622,7 +639,10 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return TypeNode.createDefaultValue(clazz); } @@ -691,7 +711,10 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return TypeNode.createDefaultValue(clazz); } @@ -734,10 +757,13 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmNull.withDefault( - elementTypeNode.createDefaultValue(language, headerSection, qualifiedName)); + elementTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName)); } @Override @@ -817,12 +843,15 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return defaultIndex == -1 ? null : elementTypeNodes[defaultIndex].createDefaultValue( - language, headerSection, qualifiedName); + frame, language, headerSection, qualifiedName); } @Override @@ -1021,9 +1050,11 @@ public abstract class TypeNode extends PklNode { } @Override - @TruffleBoundary public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return unionDefault; } @@ -1063,7 +1094,10 @@ public abstract class TypeNode extends PklNode { @Override public Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmList.EMPTY; } @@ -1152,7 +1186,10 @@ public abstract class TypeNode extends PklNode { @Override public Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmList.EMPTY; } @@ -1240,7 +1277,10 @@ public abstract class TypeNode extends PklNode { @Override public final Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmSet.EMPTY; } @@ -1332,7 +1372,10 @@ public abstract class TypeNode extends PklNode { @Override public Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { return VmMap.EMPTY; } @@ -1586,45 +1629,48 @@ public abstract class TypeNode extends PklNode { return valueTypeCastNode; } - // either (if defaultMemberValue != null): - // x: Listing // = new Listing { - // default = name -> new Foo {} - // } - // or (if defaultMemberValue == null): - // x: Listing // = new Listing { - // default = Undefined() - // } - @Override @TruffleBoundary - public final Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { - - if (valueTypeNode instanceof UnknownTypeNode) { - if (isListing()) { - return new VmListing( - VmUtils.createEmptyMaterializedFrame(), - BaseModule.getListingClass().getPrototype(), - EconomicMaps.create(), - 0); - } - - return new VmMapping( + private Object newEmptyListingOrMapping() { + if (isListing()) { + return new VmListing( VmUtils.createEmptyMaterializedFrame(), - BaseModule.getMappingClass().getPrototype(), - EconomicMaps.create()); + BaseModule.getListingClass().getPrototype(), + EconomicMaps.create(), + 0); } + return new VmMapping( + VmUtils.createEmptyMaterializedFrame(), + BaseModule.getMappingClass().getPrototype(), + EconomicMaps.create()); + } + + @TruffleBoundary + private Object newEmptyListingOrMapping(ObjectMember defaultMember) { + if (isListing()) { + return new VmListing( + VmUtils.createEmptyMaterializedFrame(), + BaseModule.getListingClass().getPrototype(), + EconomicMaps.of(Identifier.DEFAULT, defaultMember), + 0); + } + + return new VmMapping( + VmUtils.createEmptyMaterializedFrame(), + BaseModule.getMappingClass().getPrototype(), + EconomicMaps.of(Identifier.DEFAULT, defaultMember)); + } + + @TruffleBoundary + private ObjectMember createDefaultMember( + SourceSection headerSection, String qualifiedName, @Nullable Object defaultMemberValue) { var defaultMember = new ObjectMember( headerSection, headerSection, VmModifier.HIDDEN, Identifier.DEFAULT, - qualifiedName + ".default"); - - var defaultMemberValue = - valueTypeNode.createDefaultValue(language, headerSection, qualifiedName); - + VmUtils.concat(qualifiedName, ".default")); if (defaultMemberValue == null) { defaultMember.initMemberNode( new UntypedObjectMemberNode( @@ -1645,23 +1691,38 @@ public abstract class TypeNode extends PklNode { language, new FrameDescriptor(), headerSection, - defaultMember.getQualifiedName() + ".", + VmUtils.concat(defaultMember.getQualifiedName(), "."), new ConstantValueNode(defaultMemberValue)), null)); } + return defaultMember; + } - if (isListing()) { - return new VmListing( - VmUtils.createEmptyMaterializedFrame(), - BaseModule.getListingClass().getPrototype(), - EconomicMaps.of(Identifier.DEFAULT, defaultMember), - 0); + // either (if defaultMemberValue != null): + // x: Listing // = new Listing { + // default = name -> new Foo {} + // } + // or (if defaultMemberValue == null): + // x: Listing // = new Listing { + // default = Undefined() + // } + @Override + public final Object createDefaultValue( + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { + + if (valueTypeNode instanceof UnknownTypeNode) { + return newEmptyListingOrMapping(); } - return new VmMapping( - VmUtils.createEmptyMaterializedFrame(), - BaseModule.getMappingClass().getPrototype(), - EconomicMaps.of(Identifier.DEFAULT, defaultMember)); + var defaultMemberValue = + valueTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName); + + var defaultMember = createDefaultMember(headerSection, qualifiedName, defaultMemberValue); + + return newEmptyListingOrMapping(defaultMember); } protected void doEagerCheck(VirtualFrame frame, VmObject object) { @@ -2030,7 +2091,10 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { CompilerDirectives.transferToInterpreter(); throw exceptionBuilder() @@ -2441,34 +2505,41 @@ public abstract class TypeNode extends PklNode { } } - @Override @TruffleBoundary + private VmFunction newMixin(VmLanguage language, String qualifiedName) { + //noinspection ConstantConditions + return new VmFunction( + VmUtils.createEmptyMaterializedFrame(), + // Assumption: don't need to set the correct `thisValue` + // because it is guaranteed to be never accessed. + null, + 1, + new IdentityMixinNode( + language, + new FrameDescriptor(), + getSourceSection(), + qualifiedName, + typeArgumentNodes.length == 1 + ? + // shouldn't need to deepCopy() this node because it isn't used as @Child + // anywhere else + typeArgumentNodes[0] + : null), + null); + } + + @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { if (typeAlias == BaseModule.getMixinTypeAlias()) { - //noinspection ConstantConditions - return new VmFunction( - VmUtils.createEmptyMaterializedFrame(), - // Assumption: don't need to set the correct `thisValue` - // because it is guaranteed to be never accessed. - null, - 1, - new IdentityMixinNode( - language, - new FrameDescriptor(), - getSourceSection(), - qualifiedName, - typeArgumentNodes.length == 1 - ? - // shouldn't need to deepCopy() this node because it isn't used as @Child - // anywhere else - typeArgumentNodes[0] - : null), - null); + return newMixin(language, qualifiedName); } - return aliasedTypeNode.createDefaultValue(language, headerSection, qualifiedName); + return aliasedTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName); } @Override @@ -2593,9 +2664,12 @@ public abstract class TypeNode extends PklNode { @Override public @Nullable Object createDefaultValue( - VmLanguage language, SourceSection headerSection, String qualifiedName) { + VirtualFrame frame, + VmLanguage language, + SourceSection headerSection, + String qualifiedName) { - return childNode.createDefaultValue(language, headerSection, qualifiedName); + return childNode.createDefaultValue(frame, language, headerSection, qualifiedName); } public SourceSection getBaseTypeSection() { diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/MirrorFactories.java b/pkl-core/src/main/java/org/pkl/core/runtime/MirrorFactories.java index 093abd26..5078ff7b 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/MirrorFactories.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/MirrorFactories.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -184,7 +184,10 @@ public final class MirrorFactories { property -> property.getProperty().isAbstract() || property.getProperty().isExternal() - || property.getProperty().getInitializer().isUndefined() + || property + .getProperty() + .getInitializer() + .isUndefined(VmUtils.createEmptyMaterializedFrame()) ? VmNull.withoutDefault() : // get default from prototype because it's cached there diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java index d1a12c91..e36a68f0 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 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. @@ -909,4 +909,9 @@ public final class VmUtils { return frame.getArguments().length != 4 || frame.getArguments()[3] != VmUtils.SKIP_TYPECHECK_MARKER; } + + @TruffleBoundary + public static String concat(String str1, String str2) { + return str1 + str2; + } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/moduleType2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/moduleType2.pkl new file mode 100644 index 00000000..bb14ebc3 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/moduleType2.pkl @@ -0,0 +1,20 @@ +open module Foo + +prop: String + +myself: module? + +class Bar extends module { + prop2: Int +} + +output { + value = new Bar { + prop = "hi" + prop2 = 15 + myself { + prop = "hi again" + prop2 = 15 + } + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/moduleType2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/moduleType2.pcf new file mode 100644 index 00000000..50194168 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/moduleType2.pcf @@ -0,0 +1,7 @@ +prop = "hi" +myself { + prop = "hi again" + myself = null + prop2 = 15 +} +prop2 = 15