From dd40feecd2a4c24e86094c45ee9934a9b03b5fc1 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 25 Feb 2026 14:28:14 -0700 Subject: [PATCH 1/8] Ignore warnings first Signed-off-by: Tom --- .../kotlin/lang/temper/be/rust/RustBackend.kt | 2 +- .../kotlin/lang/temper/be/rust/RustExt.kt | 14 ++++++------- .../lang/temper/be/rust/RustTranslator.kt | 2 +- .../lang/temper/be/rust/RustBackendTest.kt | 21 ++++++++++++------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustBackend.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustBackend.kt index d1e64dc..f5c46ff 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustBackend.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustBackend.kt @@ -311,7 +311,7 @@ private fun MutableList.addLib( path = filePath("src", "lib.rs"), content = Rust.SourceFile( pos, - attrs = listOf(allowWarnings(pos)), + attrs = allowWarnings(pos), items = buildList { // Separate mods. declareSubmods(pos, allModKids[libraryConfiguration.libraryRoot] ?: setOf()) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt index 0e738d5..a75e854 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt @@ -43,14 +43,12 @@ import lang.temper.type2.withType * change in edition. We should investigate this more in the future, though. It would * be good to support latest expectations. */ -internal fun allowWarnings(pos: Position): Rust.AttrInner = Rust.AttrInner( - pos, - Rust.Call( - pos, - "allow".toId(pos), - listOf("dependency_on_unit_never_type_fallback", "warnings").map { it.toId(pos) }, - ), -) +internal fun allowWarnings(pos: Position) = run { + // Separate them with `warnings` first, to prevent warnings against the other on older rust. + listOf("warnings", "dependency_on_unit_never_type_fallback").map { key -> + Rust.AttrInner(pos, Rust.Call(pos, "allow".toId(pos), listOf(key.toId(pos)))) + } +} internal fun makeError(pos: Position) = Rust.Call(pos, callee = ERROR_NEW_NAME.toId(pos), args = listOf()) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index 71d7b08..c179020 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -137,7 +137,7 @@ class RustTranslator( path = makeSrcFilePath(relPath.withTemperAwareExtension("")), content = Rust.SourceFile( pos, - attrs = listOf(allowWarnings(module.pos)), + attrs = allowWarnings(module.pos), items = buildList { // Declare submodules, except for root that needs to declare in lib file. if (!isRoot) { diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index c812600..16127d7 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -67,7 +67,8 @@ class RustBackendTest { | src: { | lib.rs: { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | pub mod bar; | mod r#mod; | pub use r#mod::*; @@ -86,7 +87,8 @@ class RustBackendTest { | main.rs: "__DO_NOT_CARE__", | mod.rs: { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | use temper_core::AnyValueTrait; | use temper_core::AsAnyValue; | use temper_core::Pair; @@ -124,7 +126,8 @@ class RustBackendTest { | bar: { | mod.rs: { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | use temper_core::AnyValueTrait; | use temper_core::AsAnyValue; | use temper_core::Pair; @@ -1067,7 +1070,8 @@ class RustBackendTest { | src: { | lib.rs: { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | pub mod bar; | pub mod bob; | mod support; @@ -1086,7 +1090,8 @@ class RustBackendTest { | bar: { | mod.rs: { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | use temper_core::AnyValueTrait; | use temper_core::AsAnyValue; | use temper_core::Pair; @@ -1122,7 +1127,8 @@ class RustBackendTest { | "beth": { | "mod.rs": { | content: ``` - | #![allow(dependency_on_unit_never_type_fallback, warnings)] + | #![allow(warnings)] + | #![allow(dependency_on_unit_never_type_fallback)] | use temper_core::AnyValueTrait; | use temper_core::AsAnyValue; | use temper_core::Pair; @@ -2424,7 +2430,8 @@ private fun assertGenerateWanted(modules: List) { | "mod.rs": { | "content": |``` - |#![allow(dependency_on_unit_never_type_fallback, warnings)] + |#![allow(warnings)] + |#![allow(dependency_on_unit_never_type_fallback)] |use temper_core::AnyValueTrait; |use temper_core::AsAnyValue; |use temper_core::Pair; From fd75ab6065dfd1d1ad08cdacecd34015be66cccf Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 26 Feb 2026 11:48:43 -0700 Subject: [PATCH 2/8] Record current behavior Signed-off-by: Tom --- .../lang/temper/be/rust/RustBackendTest.kt | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index 16127d7..b077861 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1259,6 +1259,7 @@ class RustBackendTest { assertGenerateWanted( temper = """ |interface A { + | // Use different ways of declaring props. | public var prop: String; | public get thing(): String; | public set thing(that: String): Void; @@ -1276,6 +1277,17 @@ class RustBackendTest { | public greeting(): String { "Ha!" } | public spawn(): C> { new C>("", "") } |} + |// D provides alternate paths for override resolution. + |interface D extends A { + | public get thing(): String { "Hello!" } + | public set thing(value: String): Void {} + | public whatever(): String { "sure" } + |} + |// E provides indirection on type bindings to D. + |interface E extends D {} + |class F( + | public var prop: String, + |) extends B> & E {} """.trimMargin(), rust = """ |pub (crate) fn init() -> temper_core::Result<()> { @@ -1448,6 +1460,176 @@ class RustBackendTest { | } |} |temper_core::impl_any_value_trait!(C, [B, A] where T: ATrait); + |trait DTrait: temper_core::AsAnyValue + temper_core::AnyValueTrait + std::marker::Send + std::marker::Sync + ATrait { + | fn clone_boxed(& self) -> D; + | fn thing(& self) -> std::sync::Arc { + | return std::sync::Arc::new("Hello!".to_string()); + | } + | fn set_thing(& self, value__0: std::sync::Arc) {} + | fn whatever(& self) -> std::sync::Arc { + | return std::sync::Arc::new("sure".to_string()); + | } + |} + |#[derive(Clone)] + |struct D(std::sync::Arc>); + |impl D { + | pub fn new(selfish: impl DTrait + 'static) -> D { + | D(std::sync::Arc::new(selfish)) + | } + |} + |impl DTrait for D { + | fn clone_boxed(& self) -> D { + | DTrait::clone_boxed( & ( * self.0)) + | } + | fn thing(& self) -> std::sync::Arc { + | DTrait::thing( & ( * self.0)) + | } + | fn set_thing(& self, value: std::sync::Arc) { + | DTrait::set_thing( & ( * self.0), value) + | } + | fn whatever(& self) -> std::sync::Arc { + | DTrait::whatever( & ( * self.0)) + | } + |} + |impl ATrait for D { + | fn clone_boxed(& self) -> A { + | ATrait::clone_boxed( & ( * self.0)) + | } + | fn thing(& self) -> std::sync::Arc { + | ATrait::thing( & ( * self.0)) + | } + | fn set_thing(& self, value: std::sync::Arc) { + | ATrait::set_thing( & ( * self.0), value) + | } + | fn greeting(& self) -> std::sync::Arc { + | ATrait::greeting( & ( * self.0)) + | } + | fn whatever(& self) -> std::sync::Arc { + | ATrait::whatever( & ( * self.0)) + | } + | fn prop(& self) -> std::sync::Arc { + | ATrait::prop( & ( * self.0)) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | ATrait::set_prop( & ( * self.0), value) + | } + |} + |temper_core::impl_any_value_trait_for_interface!(D); + |impl std::ops::Deref for D { + | type Target = dyn DTrait; + | fn deref(& self) -> & Self::Target { + | & ( * self.0) + | } + |} + |trait ETrait: temper_core::AsAnyValue + temper_core::AnyValueTrait + std::marker::Send + std::marker::Sync + DTrait { + | fn clone_boxed(& self) -> E; + |} + |#[derive(Clone)] + |struct E(std::sync::Arc>); + |impl E { + | pub fn new(selfish: impl ETrait + 'static) -> E { + | E(std::sync::Arc::new(selfish)) + | } + |} + |impl ETrait for E { + | fn clone_boxed(& self) -> E { + | ETrait::clone_boxed( & ( * self.0)) + | } + |} + |impl DTrait for E { + | fn clone_boxed(& self) -> D { + | DTrait::clone_boxed( & ( * self.0)) + | } + | fn thing(& self) -> std::sync::Arc { + | DTrait::thing( & ( * self.0)) + | } + | fn set_thing(& self, value: std::sync::Arc) { + | DTrait::set_thing( & ( * self.0), value) + | } + | fn whatever(& self) -> std::sync::Arc { + | DTrait::whatever( & ( * self.0)) + | } + |} + |impl ATrait for E { + | fn clone_boxed(& self) -> A { + | ATrait::clone_boxed( & ( * self.0)) + | } + | fn thing(& self) -> std::sync::Arc { + | ATrait::thing( & ( * self.0)) + | } + | fn set_thing(& self, value: std::sync::Arc) { + | ATrait::set_thing( & ( * self.0), value) + | } + | fn greeting(& self) -> std::sync::Arc { + | ATrait::greeting( & ( * self.0)) + | } + | fn whatever(& self) -> std::sync::Arc { + | ATrait::whatever( & ( * self.0)) + | } + | fn prop(& self) -> std::sync::Arc { + | ATrait::prop( & ( * self.0)) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | ATrait::set_prop( & ( * self.0), value) + | } + |} + |temper_core::impl_any_value_trait_for_interface!(E); + |impl std::ops::Deref for E { + | type Target = dyn ETrait; + | fn deref(& self) -> & Self::Target { + | & ( * self.0) + | } + |} + |struct FStruct { + | prop: std::sync::Arc + |} + |#[derive(Clone)] + |pub (crate) struct F(std::sync::Arc>); + |impl F { + | pub fn new(prop__1: impl temper_core::ToArcString) -> F { + | let prop__1 = prop__1.to_arc_string(); + | let prop; + | prop = prop__1.clone(); + | let selfish = F(std::sync::Arc::new(std::sync::RwLock::new(FStruct { + | prop + | }))); + | return selfish; + | } + | pub fn prop(& self) -> std::sync::Arc { + | return self.0.read().unwrap().prop.clone(); + | } + | pub fn set_prop(& self, newProp__1: impl temper_core::ToArcString) { + | let newProp__1 = newProp__1.to_arc_string(); + | self.0.write().unwrap().prop = newProp__1.clone(); + | } + |} + |impl BTrait> for F { + | fn clone_boxed(& self) -> B> { + | B::new(self.clone()) + | } + |} + |impl ATrait for F { + | fn clone_boxed(& self) -> A { + | A::new(self.clone()) + | } + | fn prop(& self) -> std::sync::Arc { + | self.prop() + | } + | fn set_prop(& self, newProp__1: std::sync::Arc) { + | self.set_prop(newProp__1) + | } + |} + |impl ETrait for F { + | fn clone_boxed(& self) -> E { + | E::new(self.clone()) + | } + |} + |impl DTrait for F { + | fn clone_boxed(& self) -> D { + | D::new(self.clone()) + | } + |} + |temper_core::impl_any_value_trait!(F, [B>, A, E, D]); """.trimMargin(), ) } From 42b146ea06fd37ad541e3fa5552495907e2c1dcc Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 26 Feb 2026 17:01:39 -0700 Subject: [PATCH 3/8] Adjust test; work toward impl Signed-off-by: Tom --- .../lang/temper/be/rust/RustTranslator.kt | 60 +++++++++++++--- .../lang/temper/be/rust/RustBackendTest.kt | 71 ++++++++++--------- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index c179020..e98ee29 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -19,12 +19,15 @@ import lang.temper.be.tmpl.referencedNames import lang.temper.be.tmpl.splitConstructorBody import lang.temper.be.tmpl.typeOrInvalid import lang.temper.common.compatRemoveLast +import lang.temper.common.mapFirst import lang.temper.common.subListToEnd import lang.temper.frontend.ModuleNamingContext +import lang.temper.frontend.typestage.findOverrides import lang.temper.interp.importExport.STANDARD_LIBRARY_NAME import lang.temper.lexer.withTemperAwareExtension import lang.temper.library.LibraryConfigurations import lang.temper.log.FilePath +import lang.temper.log.LogSink import lang.temper.log.Position import lang.temper.log.last import lang.temper.name.BuiltinName @@ -38,6 +41,7 @@ import lang.temper.name.Temporary import lang.temper.type.Abstractness import lang.temper.type.MethodKind import lang.temper.type.MethodShape +import lang.temper.type.PropertyShape import lang.temper.type.TypeDefinition import lang.temper.type.TypeFormal import lang.temper.type.TypeShape @@ -51,6 +55,7 @@ import lang.temper.type2.NonNullType import lang.temper.type2.Nullity import lang.temper.type2.Signature2 import lang.temper.type2.Type2 +import lang.temper.type2.TypeContext2 import lang.temper.type2.TypeParamRef import lang.temper.type2.ValueFormalKind import lang.temper.type2.hackMapOldStyleToNew @@ -94,6 +99,7 @@ class RustTranslator( private var insideMutableType = false private val failVars = mutableSetOf() private val functionContextStack = mutableListOf() + private val logSink = LogSink.devNull // TODO what? private val loopLabels = mutableListOf() private val moduleInits = mutableListOf() private val moduleItems = mutableListOf() @@ -102,6 +108,7 @@ class RustTranslator( } private val testItems = mutableListOf() private val traitImports = mutableSetOf() + private val typeContext = TypeContext2() fun translateModule(): Backend.TranslatedFileSpecification { // Preprocess tops. @@ -775,8 +782,8 @@ class RustTranslator( for (supMethod in supShape.methods) { maybeAddTraitForwarder( pos, - instanceMethods, - isInterface = isInterface, + decl = decl, + instanceMethods = instanceMethods, methodKind = supMethod.methodKind, methodName = supMethod.name.displayName, returnType = supMethod.descriptor.orInvalid.returnType2, @@ -788,8 +795,8 @@ class RustTranslator( if (supProperty.getter == null) { maybeAddTraitForwarder( pos, - instanceMethods, - isInterface = isInterface, + decl = decl, + instanceMethods = instanceMethods, methodKind = MethodKind.Getter, methodName = BuiltinName("get.${supProperty.symbol.text}").displayName, superShape = supProperty, @@ -798,8 +805,8 @@ class RustTranslator( if (supProperty.setter == null && supProperty.hasSetter) { maybeAddTraitForwarder( pos, - instanceMethods, - isInterface = isInterface, + decl = decl, + instanceMethods = instanceMethods, methodKind = MethodKind.Setter, methodName = BuiltinName("set.${supProperty.symbol.text}").displayName, superShape = supProperty, @@ -815,17 +822,18 @@ class RustTranslator( private fun MutableList.maybeAddTraitForwarder( pos: Position, + decl: TmpL.TypeDeclaration, instanceMethods: Map, - isInterface: Boolean, methodKind: MethodKind, methodName: String, superShape: VisibleMemberShape, returnType: Type2? = null, ) { + val isInterface = decl.kind == TmpL.TypeDeclarationKind.Interface when { - isInterface -> buildForwarderForTrait(pos, superShape, methodKind) + isInterface -> buildForwarderFromInterfaceToTrait(pos, superShape, methodKind) else -> when (val method = instanceMethods[methodName]) { - null -> listOf() + null -> buildForwarderFromClassToTrait(pos, decl, superShape, methodKind) else -> buildForwarder(method, returnType = returnType) } }.also { addAll(it) } // only one expected here, but meh @@ -1073,12 +1081,44 @@ class RustTranslator( return translateMethodLike(method, block = block, forTrait = true, returnType = effectiveReturnType) } + private fun buildForwarderFromClassToTrait( + pos: Position, + decl: TmpL.TypeDeclaration, + superShape: VisibleMemberShape, + methodKind: MethodKind, + ): List = run { + // We're here because this class has no matching member, so walk its supertypes to match the super method. + // The method we inherit closest might be on a different branch. + val overrides = findOverrides(decl.typeShape, superShape, typeContext, logSink) + val override = overrides.find { override -> + when (val foundMember = override.superTypeMember) { + is MethodShape -> !foundMember.isPureVirtual + is PropertyShape -> when (methodKind) { + MethodKind.Getter -> foundMember.enclosingType.methods.any { foundMethod -> + !foundMethod.isPureVirtual && + foundMethod.methodKind == MethodKind.Getter && + foundMethod.name == foundMember.getter + } + MethodKind.Setter -> foundMember.enclosingType.methods.any { foundMethod -> + !foundMethod.isPureVirtual && + foundMethod.methodKind == MethodKind.Setter && + foundMethod.name == foundMember.setter + } + else -> false + } + else -> false + } + (override.superTypeMember as? MethodShape)?.isPureVirtual == false + } + listOf() + } + /** * Build a forwarder from a trait wrapper to a trait method that *isn't* * overridden in the current trait. We need this to handle methods for * which we have only frontend descriptions, not tmpl. */ - private fun buildForwarderForTrait( + private fun buildForwarderFromInterfaceToTrait( pos: Position, shape: VisibleMemberShape, methodKind: MethodKind, diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index b077861..9ff2003 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1279,15 +1279,17 @@ class RustBackendTest { |} |// D provides alternate paths for override resolution. |interface D extends A { - | public get thing(): String { "Hello!" } + | public get prop(): String { "Hello!" } + | public set prop(value: String): Void {} | public set thing(value: String): Void {} | public whatever(): String { "sure" } |} |// E provides indirection on type bindings to D. |interface E extends D {} - |class F( - | public var prop: String, - |) extends B> & E {} + |// Alternate prop/thing set/get vs D above. + |class F extends B> & E { + | public get thing(): String { "Hello!" } + |} """.trimMargin(), rust = """ |pub (crate) fn init() -> temper_core::Result<()> { @@ -1462,10 +1464,12 @@ class RustBackendTest { |temper_core::impl_any_value_trait!(C, [B, A] where T: ATrait); |trait DTrait: temper_core::AsAnyValue + temper_core::AnyValueTrait + std::marker::Send + std::marker::Sync + ATrait { | fn clone_boxed(& self) -> D; - | fn thing(& self) -> std::sync::Arc { + | fn prop(& self) -> std::sync::Arc { | return std::sync::Arc::new("Hello!".to_string()); | } - | fn set_thing(& self, value__0: std::sync::Arc) {} + | fn set_prop(& self, value__0: std::sync::Arc) {} + | fn thing(& self) -> std::sync::Arc; + | fn set_thing(& self, value__1: std::sync::Arc) {} | fn whatever(& self) -> std::sync::Arc { | return std::sync::Arc::new("sure".to_string()); | } @@ -1481,8 +1485,11 @@ class RustBackendTest { | fn clone_boxed(& self) -> D { | DTrait::clone_boxed( & ( * self.0)) | } - | fn thing(& self) -> std::sync::Arc { - | DTrait::thing( & ( * self.0)) + | fn prop(& self) -> std::sync::Arc { + | DTrait::prop( & ( * self.0)) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | DTrait::set_prop( & ( * self.0), value) | } | fn set_thing(& self, value: std::sync::Arc) { | DTrait::set_thing( & ( * self.0), value) @@ -1490,6 +1497,9 @@ class RustBackendTest { | fn whatever(& self) -> std::sync::Arc { | DTrait::whatever( & ( * self.0)) | } + | fn thing(& self) -> std::sync::Arc { + | DTrait::thing( & ( * self.0)) + | } |} |impl ATrait for D { | fn clone_boxed(& self) -> A { @@ -1540,8 +1550,11 @@ class RustBackendTest { | fn clone_boxed(& self) -> D { | DTrait::clone_boxed( & ( * self.0)) | } - | fn thing(& self) -> std::sync::Arc { - | DTrait::thing( & ( * self.0)) + | fn prop(& self) -> std::sync::Arc { + | DTrait::prop( & ( * self.0)) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | DTrait::set_prop( & ( * self.0), value) | } | fn set_thing(& self, value: std::sync::Arc) { | DTrait::set_thing( & ( * self.0), value) @@ -1549,6 +1562,9 @@ class RustBackendTest { | fn whatever(& self) -> std::sync::Arc { | DTrait::whatever( & ( * self.0)) | } + | fn thing(& self) -> std::sync::Arc { + | DTrait::thing( & ( * self.0)) + | } |} |impl ATrait for E { | fn clone_boxed(& self) -> A { @@ -1580,27 +1596,16 @@ class RustBackendTest { | & ( * self.0) | } |} - |struct FStruct { - | prop: std::sync::Arc - |} + |struct FStruct {} |#[derive(Clone)] - |pub (crate) struct F(std::sync::Arc>); + |pub (crate) struct F(std::sync::Arc); |impl F { - | pub fn new(prop__1: impl temper_core::ToArcString) -> F { - | let prop__1 = prop__1.to_arc_string(); - | let prop; - | prop = prop__1.clone(); - | let selfish = F(std::sync::Arc::new(std::sync::RwLock::new(FStruct { - | prop - | }))); - | return selfish; - | } - | pub fn prop(& self) -> std::sync::Arc { - | return self.0.read().unwrap().prop.clone(); + | pub fn thing(& self) -> std::sync::Arc { + | return std::sync::Arc::new("Hello!".to_string()); | } - | pub fn set_prop(& self, newProp__1: impl temper_core::ToArcString) { - | let newProp__1 = newProp__1.to_arc_string(); - | self.0.write().unwrap().prop = newProp__1.clone(); + | pub fn new() -> F { + | let selfish = F(std::sync::Arc::new(FStruct {})); + | return selfish; | } |} |impl BTrait> for F { @@ -1612,11 +1617,8 @@ class RustBackendTest { | fn clone_boxed(& self) -> A { | A::new(self.clone()) | } - | fn prop(& self) -> std::sync::Arc { - | self.prop() - | } - | fn set_prop(& self, newProp__1: std::sync::Arc) { - | self.set_prop(newProp__1) + | fn thing(& self) -> std::sync::Arc { + | self.thing() | } |} |impl ETrait for F { @@ -1628,6 +1630,9 @@ class RustBackendTest { | fn clone_boxed(& self) -> D { | D::new(self.clone()) | } + | fn thing(& self) -> std::sync::Arc { + | self.thing() + | } |} |temper_core::impl_any_value_trait!(F, [B>, A, E, D]); """.trimMargin(), From f3d7e5072b1cd894034d08d41408addad8a769c8 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 26 Feb 2026 17:09:26 -0700 Subject: [PATCH 4/8] Add missed override linker factoring Signed-off-by: Tom --- .../frontend/typestage/OverrideLinker.kt | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/OverrideLinker.kt b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/OverrideLinker.kt index 866e0d5..66dd32a 100644 --- a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/OverrideLinker.kt +++ b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/OverrideLinker.kt @@ -29,20 +29,33 @@ import lang.temper.type2.hackMapOldStyleToNew import lang.temper.type2.mapType import kotlin.math.min +/** + * Updates [member]'s overridden members with those found from its enclosing type. + */ internal fun linkOverrides( member: VisibleMemberShape, typeContext: TypeContext2, logSink: LogSink, ) { + member.overriddenMembers = findOverrides(member.enclosingType, member, typeContext, logSink) +} + +/** + * Return overrides for the given [member] shape in the [enclosingTypeShape] context. + * Doesn't require that the member actually be defined in the given enclosing type. + */ +fun findOverrides( + enclosingTypeShape: TypeShape, + member: VisibleMemberShape, + typeContext: TypeContext2, + logSink: LogSink, +): Set { if (member.visibility == Visibility.Private || member is StaticPropertyShape) { // Private members and statics override nothing nor are overridden. // Dispatch to them is non-virtual. - member.overriddenMembers = emptySet() - return + return emptySet() } val overriddenMembers = mutableSetOf() - - val enclosingTypeShape = member.enclosingType val enclosingType = MkType.nominal( enclosingTypeShape, enclosingTypeShape.typeParameters.map { MkType.nominal(it.definition, emptyList()) }, @@ -61,8 +74,7 @@ internal fun linkOverrides( true } } - - member.overriddenMembers = overriddenMembers.toSet() + return overriddenMembers.toSet() } private fun overriddenIn( From d0ab6fc1e07193c238bfde4cde8352a328c80c63 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 27 Feb 2026 08:16:09 -0700 Subject: [PATCH 5/8] Dispatch super calls from classes Signed-off-by: Tom --- .../lang/temper/be/rust/RustTranslator.kt | 105 ++++++++++-------- .../lang/temper/be/rust/RustBackendTest.kt | 36 ++++++ 2 files changed, 93 insertions(+), 48 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index e98ee29..c774d1f 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -19,7 +19,6 @@ import lang.temper.be.tmpl.referencedNames import lang.temper.be.tmpl.splitConstructorBody import lang.temper.be.tmpl.typeOrInvalid import lang.temper.common.compatRemoveLast -import lang.temper.common.mapFirst import lang.temper.common.subListToEnd import lang.temper.frontend.ModuleNamingContext import lang.temper.frontend.typestage.findOverrides @@ -834,7 +833,7 @@ class RustTranslator( isInterface -> buildForwarderFromInterfaceToTrait(pos, superShape, methodKind) else -> when (val method = instanceMethods[methodName]) { null -> buildForwarderFromClassToTrait(pos, decl, superShape, methodKind) - else -> buildForwarder(method, returnType = returnType) + else -> buildForwarder(method, returnType) } }.also { addAll(it) } // only one expected here, but meh } @@ -1090,27 +1089,34 @@ class RustTranslator( // We're here because this class has no matching member, so walk its supertypes to match the super method. // The method we inherit closest might be on a different branch. val overrides = findOverrides(decl.typeShape, superShape, typeContext, logSink) - val override = overrides.find { override -> + val override = overrides.find overrides@{ override -> when (val foundMember = override.superTypeMember) { is MethodShape -> !foundMember.isPureVirtual is PropertyShape -> when (methodKind) { - MethodKind.Getter -> foundMember.enclosingType.methods.any { foundMethod -> - !foundMethod.isPureVirtual && - foundMethod.methodKind == MethodKind.Getter && - foundMethod.name == foundMember.getter + MethodKind.Getter -> foundMember.getter + MethodKind.Setter -> foundMember.setter + else -> return@overrides false + }.let { foundName -> + // Interfaces can only provide property implementations with methods, so look at those. + foundMember.enclosingType.methods.any { method -> + !method.isPureVirtual && method.methodKind == methodKind && method.name == foundName } - MethodKind.Setter -> foundMember.enclosingType.methods.any { foundMethod -> - !foundMethod.isPureVirtual && - foundMethod.methodKind == MethodKind.Setter && - foundMethod.name == foundMember.setter - } - else -> false } else -> false } - (override.superTypeMember as? MethodShape)?.isPureVirtual == false } - listOf() + // Having selected an override, forward to it with simple self. + val targetType = override?.superTypeMember?.enclosingType + buildForwarderToTrait(pos, targetType, superShape, methodKind) result@{ traitType, methodId, argIds -> + Rust.Call( + pos, + callee = traitType.extendWith(methodId.deepCopy()), + args = buildList { + add("self".toKeyId(pos)) + addAll(argIds) + }, + ) + } } /** @@ -1122,50 +1128,60 @@ class RustTranslator( pos: Position, shape: VisibleMemberShape, methodKind: MethodKind, + ): List = run { + // From trait wrapper to trait, just forward the call with the unwrapped innards. + buildForwarderToTrait(pos, shape.enclosingType, shape, methodKind) result@{ traitType, methodId, argIds -> + Rust.Call( + pos, + callee = traitType.extendWith(methodId.deepCopy()), + args = buildList { + add("self".toKeyId(pos).member("0", notMethod = true).deref().ref()) + addAll(argIds) + }, + ) + } + } + + private fun buildForwarderToTrait( + pos: Position, + targetType: TypeShape?, + shape: VisibleMemberShape, + methodKind: MethodKind, + /** Trait type, method id, and arg ids, all ready to be used. */ + buildResult: (Rust.Path, Rust.Id, List) -> Rust.Expr?, ): List = run { val selfParam = Rust.RefType(pos, "self".toKeyId(pos)) - val selfArg = "self".toKeyId(pos).member("0", notMethod = true).deref().ref() - val enclosingType = - (translateTypeDefinition(shape.enclosingType, pos) as? Rust.Path)?.suffixed(TRAIT_NAME_SUFFIX) + val traitType = targetType?.let { + (translateTypeDefinition(targetType, pos) as? Rust.Path)?.suffixed(TRAIT_NAME_SUFFIX) + } when (methodKind) { MethodKind.Normal -> { val method = shape as MethodShape val methodId = translateIdFromName(pos, method.name as ResolvedName, NameStyle.Snake) val sig = method.descriptor ?: return listOf() - val argNames = (1.. + "arg$arg".toId(pos) + } Rust.Function( pos, - id = methodId, + id = methodId.deepCopy(), params = buildList { add(selfParam) var index = 0 for (paramType in sig.requiredInputTypes.subListToEnd(1)) { - val paramName = argNames[index++].toId(pos) + val paramName = argIds[index++].deepCopy() val translatedType = translateType(paramType, pos) add(Rust.FunctionParam(pos, paramName, translatedType)) } for (paramType in sig.optionalInputTypes) { - val paramName = argNames[index++].toId(pos) + val paramName = argIds[index++].deepCopy() val translatedType = translateType(paramType, pos).option() add(Rust.FunctionParam(pos, paramName, translatedType)) } }, returnType = method.descriptor?.let { translateType(it.returnType2, pos = pos) }, - block = Rust.Block( - pos, - result = enclosingType?.let { type -> - Rust.Call( - pos, - callee = type.extendWith(methodId.deepCopy()), - args = buildList { - add(selfArg) - for (argName in argNames) { - add(argName.toId(pos)) - } - }, - ) - }, - ), + block = Rust.Block(pos, result = traitType?.let { buildResult(it, methodId, argIds) }), ) } MethodKind.Getter -> { @@ -1175,15 +1191,12 @@ class RustTranslator( is Type2 -> descriptor else -> null }?.let { translateType(it, pos = pos) } - val call = enclosingType?.let { type -> - Rust.Call(pos, type.extendWith(methodId.deepCopy()), listOf(selfArg)) - } Rust.Function( pos, - id = methodId, + id = methodId.deepCopy(), params = listOf(selfParam), returnType = returnType, - block = Rust.Block(pos, result = call), + block = Rust.Block(pos, result = traitType?.let { buildResult(it, methodId, listOf()) }), ) } MethodKind.Setter -> { @@ -1195,15 +1208,11 @@ class RustTranslator( }?.let { translateType(it, pos = pos) } // We don't have param names here, so invent one. val value = "value".toId(pos) - val call = enclosingType?.let { type -> - val args = listOf(selfArg, value.deepCopy()) - Rust.Call(pos, type.extendWith(methodId.deepCopy()), args) - } Rust.Function( pos, id = methodId, - params = listOf(selfParam, Rust.FunctionParam(pos, value, propertyType)), - block = Rust.Block(pos, result = call), + params = listOf(selfParam, Rust.FunctionParam(pos, value.deepCopy(), propertyType)), + block = Rust.Block(pos, result = traitType?.let { buildResult(it, methodId, listOf(value)) }), ) } else -> return listOf() diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index 9ff2003..ef5de70 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1440,6 +1440,9 @@ class RustBackendTest { | fn clone_boxed(& self) -> B { | B::new(self.clone()) | } + | fn whatever(& self) -> std::sync::Arc { + | BTrait::whatever(self) + | } |} |impl ATrait for C { | fn clone_boxed(& self) -> A { @@ -1454,6 +1457,9 @@ class RustBackendTest { | fn greeting(& self) -> std::sync::Arc { | self.greeting() | } + | fn whatever(& self) -> std::sync::Arc { + | BTrait::whatever(self) + | } | fn prop(& self) -> std::sync::Arc { | self.prop() | } @@ -1612,6 +1618,9 @@ class RustBackendTest { | fn clone_boxed(& self) -> B> { | B::new(self.clone()) | } + | fn whatever(& self) -> std::sync::Arc { + | BTrait::whatever(self) + | } |} |impl ATrait for F { | fn clone_boxed(& self) -> A { @@ -1620,6 +1629,21 @@ class RustBackendTest { | fn thing(& self) -> std::sync::Arc { | self.thing() | } + | fn set_thing(& self, value: std::sync::Arc) { + | DTrait::set_thing(self, value) + | } + | fn greeting(& self) -> std::sync::Arc { + | ATrait::greeting(self) + | } + | fn whatever(& self) -> std::sync::Arc { + | BTrait::whatever(self) + | } + | fn prop(& self) -> std::sync::Arc { + | DTrait::prop(self) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | DTrait::set_prop(self, value) + | } |} |impl ETrait for F { | fn clone_boxed(& self) -> E { @@ -1630,6 +1654,18 @@ class RustBackendTest { | fn clone_boxed(& self) -> D { | D::new(self.clone()) | } + | fn prop(& self) -> std::sync::Arc { + | DTrait::prop(self) + | } + | fn set_prop(& self, value: std::sync::Arc) { + | DTrait::set_prop(self, value) + | } + | fn set_thing(& self, value: std::sync::Arc) { + | DTrait::set_thing(self, value) + | } + | fn whatever(& self) -> std::sync::Arc { + | BTrait::whatever(self) + | } | fn thing(& self) -> std::sync::Arc { | self.thing() | } From 1239c8a7de8c677b402b9ca5b36556deb0c1c79e Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 27 Feb 2026 08:51:34 -0700 Subject: [PATCH 6/8] Say maker instead of builder Signed-off-by: Tom --- .../lang/temper/be/rust/RustTranslator.kt | 25 +++++++++++------- .../lang/temper/be/rust/RustBackendTest.kt | 26 +++++++++---------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index c774d1f..421db5e 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -304,7 +304,14 @@ class RustTranslator( } } - private fun processForClassBuilder( + /** + * Generate helpers for using named fields for making new class instances. + * These are called `...Maker` rather than `...Builder` because it's less + * likely to interfere with user-named Builder types. Temper has a style of + * naming things `...Builder`, so Temper users might reach for that naming + * easily in their own types, and we want to reduce collision risk. + */ + private fun processForClassMaker( fn: TmpL.FunctionDeclarationOrMethod, enclosingType: Rust.Type? = null, generics: List = listOf(), @@ -315,8 +322,8 @@ class RustTranslator( fn.parameters.parameters.count { it.name != fn.parameters.thisName } <= 1 && return // And for now, skip those with rest parameters. TODO Extract to list value? fn.parameters.restParameter != null && return - // Build the builder. - // Here we make `WhateverBuilder` for requireds and/or `WhateverBuilderOptions` structs for optionals. + // Build the maker. + // Here we make `WhateverMaker` for requireds and/or `WhateverMakerOptions` structs for optionals. // Alternatively, could make a Java-style builder, which is common in Rust, but it takes less advantage of // standard static checking that we get with pub struct fields. val pos = fn.pos @@ -327,7 +334,7 @@ class RustTranslator( else -> enclosingType as Rust.Id } // TODO Ensure unique names. - val builderId = "${targetId.outName.outputNameText}Builder".toId(targetId.pos) + val builderId = "${targetId.outName.outputNameText}Maker".toId(targetId.pos) val optionsId = "${targetId.outName.outputNameText}Options".toId(targetId.pos) // Figure out if we have requireds and/or optionals. // We need to separate these in Rust because requireds often can't Default. @@ -395,7 +402,7 @@ class RustTranslator( items = buildList { Rust.Function( pos, - id = "build".toId(pos), + id = "make".toId(pos), // Pass self by move on purpose in these, so we can avoid cloning. // We make builder types Clone in case anyone badly wants copies on their own. params = listOf(self), @@ -422,7 +429,7 @@ class RustTranslator( items = buildList { Rust.Function( pos, - id = "build".toId(pos), + id = "make".toId(pos), params = listOf(self), returnType = rustReturnType, block = Rust.Block( @@ -432,7 +439,7 @@ class RustTranslator( optionals.isEmpty() -> callNew(requireds) // Delegate to the with-optionals builder. else -> self.deepCopy().methodCall( - key = "build_with", + key = "make_with", args = listOf(makePath(pos, "std", "default", "Default", "default").call()), ) }, @@ -442,7 +449,7 @@ class RustTranslator( val optionsArg = "options".toId(pos) Rust.Function( pos, - id = "build_with".toId(pos), + id = "make_with".toId(pos), generics = optionalGenerics.deepCopy(), params = listOf( self.deepCopy(), @@ -1929,7 +1936,7 @@ class RustTranslator( enclosingTypePub != null && enclosingTypePub.scope == null ) { - processForClassBuilder(constructor, enclosingType, generics, returnType = returnType) + processForClassMaker(constructor, enclosingType, generics, returnType = returnType) } // Actually translate the constructor now, with all our adjustments. return translateFunctionDeclarationOrMethod( diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index ef5de70..c84cba1 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1193,11 +1193,11 @@ class RustBackendTest { |#[derive(Clone)] |pub struct C(std::sync::Arc>); |#[derive(Clone)] - |pub struct CBuilder { + |pub struct CMaker { | pub x: std::sync::Arc, pub y: std::sync::Arc |} - |impl CBuilder { - | pub fn build(self) -> C { + |impl CMaker { + | pub fn make(self) -> C { | C::new(self.x, self.y) | } |} @@ -1768,7 +1768,7 @@ class RustBackendTest { } @Test - fun needlesslyGenericBuilder() { + fun needlesslyGenericMaker() { assertGenerateWanted( temper = """ |export class Ha(public i: Int, public j: Int) {} @@ -1786,11 +1786,11 @@ class RustBackendTest { |#[derive(Clone)] |pub struct Ha(std::sync::Arc>); |#[derive(Clone)] - |pub struct HaBuilder { + |pub struct HaMaker { | pub i: i32, pub j: i32 |} - |impl HaBuilder { - | pub fn build(self) -> Ha { + |impl HaMaker { + | pub fn make(self) -> Ha { | Ha::new(self.i, self.j) | } |} @@ -1845,14 +1845,14 @@ class RustBackendTest { | pub i: Option |} |#[derive(Clone)] - |pub struct HiBuilder { + |pub struct HiMaker { | pub t: Option, pub u: U |} - |impl HiBuilder { - | pub fn build(self) -> Hi { - | self.build_with(std::default::Default::default()) + |impl HiMaker { + | pub fn make(self) -> Hi { + | self.make_with(std::default::Default::default()) | } - | pub fn build_with(self, options: HiOptions) -> Hi { + | pub fn make_with(self, options: HiOptions) -> Hi { | Hi::new(self.t, self.u, options.i) | } |} @@ -2059,7 +2059,7 @@ class RustBackendTest { | pub x: Option, pub y: Option |} |impl Vec2Options { - | pub fn build(self) -> Vec2 { + | pub fn make(self) -> Vec2 { | Vec2::new(self.x, self.y) | } |} From fe54b7b583af682f856913c6cc10750a22431c29 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 27 Feb 2026 14:21:38 -0700 Subject: [PATCH 7/8] Don't do infinite recursion Signed-off-by: Tom --- .../lang/temper/be/rust/RustTranslator.kt | 4 ++++ .../lang/temper/be/rust/RustBackendTest.kt | 18 ------------------ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index 421db5e..6f1e57a 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -1114,6 +1114,10 @@ class RustTranslator( } // Having selected an override, forward to it with simple self. val targetType = override?.superTypeMember?.enclosingType + if (targetType == superShape.enclosingType) { + // Just let the trait handle this one directly. Self-call here is infinite recursion. + return listOf() + } buildForwarderToTrait(pos, targetType, superShape, methodKind) result@{ traitType, methodId, argIds -> Rust.Call( pos, diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index c84cba1..e108787 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1440,9 +1440,6 @@ class RustBackendTest { | fn clone_boxed(& self) -> B { | B::new(self.clone()) | } - | fn whatever(& self) -> std::sync::Arc { - | BTrait::whatever(self) - | } |} |impl ATrait for C { | fn clone_boxed(& self) -> A { @@ -1618,9 +1615,6 @@ class RustBackendTest { | fn clone_boxed(& self) -> B> { | B::new(self.clone()) | } - | fn whatever(& self) -> std::sync::Arc { - | BTrait::whatever(self) - | } |} |impl ATrait for F { | fn clone_boxed(& self) -> A { @@ -1632,9 +1626,6 @@ class RustBackendTest { | fn set_thing(& self, value: std::sync::Arc) { | DTrait::set_thing(self, value) | } - | fn greeting(& self) -> std::sync::Arc { - | ATrait::greeting(self) - | } | fn whatever(& self) -> std::sync::Arc { | BTrait::whatever(self) | } @@ -1654,15 +1645,6 @@ class RustBackendTest { | fn clone_boxed(& self) -> D { | D::new(self.clone()) | } - | fn prop(& self) -> std::sync::Arc { - | DTrait::prop(self) - | } - | fn set_prop(& self, value: std::sync::Arc) { - | DTrait::set_prop(self, value) - | } - | fn set_thing(& self, value: std::sync::Arc) { - | DTrait::set_thing(self, value) - | } | fn whatever(& self) -> std::sync::Arc { | BTrait::whatever(self) | } From 5bcc253543719fc312d4738a450d95ffd1ff6df1 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 2 Mar 2026 05:41:54 -0700 Subject: [PATCH 8/8] Revert builder rename for now Signed-off-by: Tom --- .../lang/temper/be/rust/RustTranslator.kt | 25 +++++++----------- .../lang/temper/be/rust/RustBackendTest.kt | 26 +++++++++---------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index 6f1e57a..6e6d975 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -304,14 +304,7 @@ class RustTranslator( } } - /** - * Generate helpers for using named fields for making new class instances. - * These are called `...Maker` rather than `...Builder` because it's less - * likely to interfere with user-named Builder types. Temper has a style of - * naming things `...Builder`, so Temper users might reach for that naming - * easily in their own types, and we want to reduce collision risk. - */ - private fun processForClassMaker( + private fun processForClassBuilder( fn: TmpL.FunctionDeclarationOrMethod, enclosingType: Rust.Type? = null, generics: List = listOf(), @@ -322,8 +315,8 @@ class RustTranslator( fn.parameters.parameters.count { it.name != fn.parameters.thisName } <= 1 && return // And for now, skip those with rest parameters. TODO Extract to list value? fn.parameters.restParameter != null && return - // Build the maker. - // Here we make `WhateverMaker` for requireds and/or `WhateverMakerOptions` structs for optionals. + // Build the builder. + // Here we make `WhateverBuilder` for requireds and/or `WhateverOptions` structs for optionals. // Alternatively, could make a Java-style builder, which is common in Rust, but it takes less advantage of // standard static checking that we get with pub struct fields. val pos = fn.pos @@ -334,7 +327,7 @@ class RustTranslator( else -> enclosingType as Rust.Id } // TODO Ensure unique names. - val builderId = "${targetId.outName.outputNameText}Maker".toId(targetId.pos) + val builderId = "${targetId.outName.outputNameText}Builder".toId(targetId.pos) val optionsId = "${targetId.outName.outputNameText}Options".toId(targetId.pos) // Figure out if we have requireds and/or optionals. // We need to separate these in Rust because requireds often can't Default. @@ -402,7 +395,7 @@ class RustTranslator( items = buildList { Rust.Function( pos, - id = "make".toId(pos), + id = "build".toId(pos), // Pass self by move on purpose in these, so we can avoid cloning. // We make builder types Clone in case anyone badly wants copies on their own. params = listOf(self), @@ -429,7 +422,7 @@ class RustTranslator( items = buildList { Rust.Function( pos, - id = "make".toId(pos), + id = "build".toId(pos), params = listOf(self), returnType = rustReturnType, block = Rust.Block( @@ -439,7 +432,7 @@ class RustTranslator( optionals.isEmpty() -> callNew(requireds) // Delegate to the with-optionals builder. else -> self.deepCopy().methodCall( - key = "make_with", + key = "build_with", args = listOf(makePath(pos, "std", "default", "Default", "default").call()), ) }, @@ -449,7 +442,7 @@ class RustTranslator( val optionsArg = "options".toId(pos) Rust.Function( pos, - id = "make_with".toId(pos), + id = "build_with".toId(pos), generics = optionalGenerics.deepCopy(), params = listOf( self.deepCopy(), @@ -1940,7 +1933,7 @@ class RustTranslator( enclosingTypePub != null && enclosingTypePub.scope == null ) { - processForClassMaker(constructor, enclosingType, generics, returnType = returnType) + processForClassBuilder(constructor, enclosingType, generics, returnType = returnType) } // Actually translate the constructor now, with all our adjustments. return translateFunctionDeclarationOrMethod( diff --git a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt index e108787..540e0a1 100644 --- a/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt +++ b/be-rust/src/commonTest/kotlin/lang/temper/be/rust/RustBackendTest.kt @@ -1193,11 +1193,11 @@ class RustBackendTest { |#[derive(Clone)] |pub struct C(std::sync::Arc>); |#[derive(Clone)] - |pub struct CMaker { + |pub struct CBuilder { | pub x: std::sync::Arc, pub y: std::sync::Arc |} - |impl CMaker { - | pub fn make(self) -> C { + |impl CBuilder { + | pub fn build(self) -> C { | C::new(self.x, self.y) | } |} @@ -1750,7 +1750,7 @@ class RustBackendTest { } @Test - fun needlesslyGenericMaker() { + fun needlesslyGenericBuilder() { assertGenerateWanted( temper = """ |export class Ha(public i: Int, public j: Int) {} @@ -1768,11 +1768,11 @@ class RustBackendTest { |#[derive(Clone)] |pub struct Ha(std::sync::Arc>); |#[derive(Clone)] - |pub struct HaMaker { + |pub struct HaBuilder { | pub i: i32, pub j: i32 |} - |impl HaMaker { - | pub fn make(self) -> Ha { + |impl HaBuilder { + | pub fn build(self) -> Ha { | Ha::new(self.i, self.j) | } |} @@ -1827,14 +1827,14 @@ class RustBackendTest { | pub i: Option |} |#[derive(Clone)] - |pub struct HiMaker { + |pub struct HiBuilder { | pub t: Option, pub u: U |} - |impl HiMaker { - | pub fn make(self) -> Hi { - | self.make_with(std::default::Default::default()) + |impl HiBuilder { + | pub fn build(self) -> Hi { + | self.build_with(std::default::Default::default()) | } - | pub fn make_with(self, options: HiOptions) -> Hi { + | pub fn build_with(self, options: HiOptions) -> Hi { | Hi::new(self.t, self.u, options.i) | } |} @@ -2041,7 +2041,7 @@ class RustBackendTest { | pub x: Option, pub y: Option |} |impl Vec2Options { - | pub fn make(self) -> Vec2 { + | pub fn build(self) -> Vec2 { | Vec2::new(self.x, self.y) | } |}