compiler-387.md
August 1, 2026 ยท View on GitHub
Problem
The C# writer can emit a no-arg member either as a C# property or as a method, selected by
--no-properties / --methods (one field, CSharpWriter.NoProperties). Only the method form
ships. The property form is a second output shape nobody wants, nobody tests deliberately, and
every writer site that spells a member has to branch on it.
Worse, the two shapes are not independent in the code. CSharpWriterExtensions.ToCSharp rejected
--no-properties without --scalar=float, so NoProperties and ScalarErase were equivalent in
practice and a dozen sites read NoProperties when what they actually meant was "the scalar
wrapper types are erased to native primitives". Those sites emit the wrong C# the moment the two
come apart โ which is exactly what the forward shipping direction requires: the 2026-08-01 decision
(decc091) keeps wrapper scalars and no erasure, so
generated/Plato.Generated.Foundation.Unoptimized could not use --no-properties at all and was
still emitting properties.
Done means
- Every
NoPropertiestest classified as property-vs-method spelling or as scalar erasure, and the erasure ones rewritten to testScalarErase. - The
--no-properties requires --scalar=floatguard is gone and method-form emission over wrapper scalars generates and compiles. -
--no-properties/--methodsCLI flags, thenoProperties/methodsOnlyparameters onToCSharp, andCSharpWriter.NoPropertiesdeleted; every remaining site collapsed to its method-form branch. -
--static-abstract's dependency guard on--no-propertiesdeleted (the flag stays). - Tests that exist only to cover the property-ful variant removed.
-
generated/Plato.Generated.Foundation.Unoptimizedregenerated, building clean, with its no-arg members emitted as methods; its.csprojheader no longer claims it cannot use the method form. - ADR recorded at
tracker/decisions/2026-08-01-property-free-emission-is-unconditional.md.
Out of scope
CSharpWriter.PrimitiveSurfaceOverrides and StructSurfacePropertyNames stay. They record where
the HANDWRITTEN runtime (src/Plato.Intrinsics) still spells a member as a property, which is a
fact about the runtime, not about this flag. Converting the runtime to method form is plato-331 and
is what would empty that table.