Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

0.6.8 Legacy Compatibility and Migration

This appendix is the reference for public behavior inherited from sigil-stitch 0.6.8. It explains what remains available, where compatibility is intentionally restricted, and how callers and external language adapters move to the current declaration model.

The declaration-lowering design defines the current ownership model. This appendix documents the compatibility bridge; it does not extend that bridge or define a second architecture.

Compatibility Boundary

In this guide, legacy means a public declaration API, serialized contract, or adapter hook that was available in 0.6.8. It does not include capability, intent, or validated-view concepts introduced after 0.6.8.

The compatibility contract is:

  • Public 0.6.8 declaration surfaces remain available during 0.7 unless an explicit compatibility decision says otherwise.
  • Legacy grammar-oriented APIs are deprecated so new use is visible at compile time. They may still be read by a frozen compatibility lowerer.
  • Existing external CodeLang implementations inherit permissive capability profiles and provided compatibility lowerers.
  • Compatibility preserves valid old behavior when the semantic input can prove it. It does not require a built-in adapter to keep generating malformed or unverifiable target code.
  • Concepts introduced after 0.6.8 may change without another compatibility layer.
  • Requiring strict profiles or removing provided compatibility lowerers is a separate 0.8 decision, not an automatic consequence of deprecation.

Deprecated does not mean that the shared grammar model is still extensible. Do not add a field, flag, or enum variant to a legacy configuration type for new syntax.

Which Path Applies?

ReaderCurrent pathCompatibility responsibility
Ordinary builder userUse semantic builders and owner-aware TypeSpec compositionReplace deprecated aliases and direct facades when the owner affects validity
Existing 0.6.8 external adapterProvided permissive profiles and frozen lowerers keep the adapter source-compatibleMigrate one declaration family at a time and retain output-parity tests
New external adapterDeclare strict capabilities and implement complete validate_* / lower_* seamsDo not model new grammar through deprecated configuration
Built-in adapterExact strict profiles and language-local loweringNever consult migrated-family legacy grammar outside compatibility code

Current Migration State

Types, functions, field sequences, computed properties, and enum-variant sequences use complete language-owned lowering for every built-in adapter. TypeSpec validates one complete declaration, constructs ValidatedType with validated children, and delegates once to CodeLang::lower_type().

The compatibility bridge restores the exact 0.6.8 source signatures touched by this migration and marks the shared grammar surface deprecated. A checked external-adapter fixture overrides the complete old trait surface, the finite documented TypeName JSON set is checked as serde_json::Value, and cargo-semver-checks 0.50.0 currently reports no unapproved break from tag 0.6.8. The compatibility manifest and fixtures live under tests/compatibility/.

Type expressions now use complete fallible RendererLang::lower_type_name() implementations for every built-in adapter before import collection. The provided default is only the frozen type-presentation bridge for external adapters written against 0.6.8.

Complete-set fallible import resolution and language-local quote handling are also implemented. Built-in declaration-generic grammar has moved out of GenericSyntaxConfig. Final rendering now calls indent_unit(), render_statement_end(), render_block_open(), render_block_close(), and render_branch_transition() directly. Every built-in adapter owns all five operations. Their provided defaults are the only renderer path that interprets legacy block configuration and hooks for an unchanged 0.6.8 external adapter. The source-read inventory below names each retained compatibility reader.

The provided external-adapter lowerers remain private implementation details. They freeze 0.6.8 behavior; they are not examples for new adapters.

Current Configuration-Read Inventory

This inventory distinguishes current source reads from the accepted target state. A built-in method that returns a legacy config is a provider, not by itself evidence that the current built-in path consumes that config. Update the inventory whenever a reader moves behind a frozen compatibility boundary.

Shared surfaceCurrent production readersClassificationRetirement owner and retained boundary
TypePresentationConfig, TypePresentation, FunctionPresentation, AssociatedTypeStyle, BoundsPresentation, WildcardPresentation; RendererLang::type_presentation()src/type_name_lowering/compatibility.rs and the deprecated direct document facade in src/type_name_render.rsCompatibility-only type grammarBuilt-ins implement complete local lower_type_name() operations; only the frozen 0.6.8 default and direct compatibility facade retain the matrix
RendererLang::module_separator()src/type_name_lowering/compatibility.rs and the deprecated direct document facade in src/type_name_render.rsCompatibility-only qualified-name grammarBuilt-ins own qualified-name spelling; the old accessor remains only in the frozen type bridge and direct facade
GenericSyntaxConfig; RendererLang::generic_syntax() in type renderingsrc/type_name_lowering/compatibility.rs and the deprecated direct document facade in src/type_name_render.rsCompatibility-only type grammarBuilt-ins own generic type application locally; the frozen bridge and direct facade retain the old delimiters and placement
GenericSyntaxConfig; RendererLang::generic_syntax() in declarationssrc/spec/where_spec.rs, src/lang/function_lowering/compatibility.rs, src/lang/type_lowering/compatibility.rs, src/lang/compatibility_markers.rs, and the deprecated direct newtype facades in src/lang/{go,kotlin,scala}.rsCompatibility-only declaration grammarBuilt-in complete lowerers own type-parameter, bound, lifetime, kind, context-bound, and constraint-clause grammar; the named compatibility modules and direct facades retain the frozen 0.6.8 read
BlockSyntaxConfig::indent_unitsrc/spec/where_spec.rs reads it only in deprecated direct where-clause helpers; compatibility lowerers and the provided renderer-event defaults retain their bridge readsCompatibility-only declaration and renderer behaviorindent_unit() owns final-renderer indentation and built-in declaration lowerers use target-local indentation; compatibility paths retain the frozen field
BlockSyntaxConfig::{uses_semicolons, block_open, block_close, close_on_transition}Only compatibility lowerers and the provided renderer-event defaults consume these fields in productionCompatibility-only renderer and declaration grammarComplete renderer events and declaration lowerers own built-in grammar; frozen compatibility paths continue to interpret old adapters
BlockSyntaxConfig::{field_terminator, type_close_terminator, bases_close}Only src/lang/field_lowering/compatibility.rs and src/lang/type_lowering/compatibility.rs consume these fields in productionCompatibility-only declaration grammarNo current replacement config; complete declaration lowerers own these bytes locally and the old fields remain frozen
FunctionSyntaxConfig, OptionalFieldStyle, PropertyStyle, and property_getter_keyword()The function, field, property, and type compatibility modules consume the applicable surfacesCompatibility-only declaration grammarAlready outside built-in complete lowerers; retain only for the deprecated 0.6.8 bridge
TypeDeclSyntaxConfigThe function, field, property, and type compatibility modules read it; deprecated ParameterSpec::emit_into() also reads it for the direct 0.6.8 parameter facadeCompatibility-only declaration grammarComplete built-in lowerers already own these bytes; retain the reads only in frozen compatibility modules and the deprecated direct facade
EnumAndAnnotationConfig and VariantValueFormatThe function, field, property, type, and variant compatibility modules read them; AnnotationSpec::emit_with() and deprecated ParameterSpec::emit_into() retain direct 0.6.8 facade behavior; permissive variant dispatch reads variants_before_fields through the variant compatibility moduleCompatibility-only annotation, parameter, and variant grammarComplete built-in lowerers use emit_with_syntax() and target-local variant grammar; retain shared reads only at the named compatibility boundaries
Shared QuoteStyle, the three public quote_style fields, and with_quote_style()One narrow helper in each of TypeScript, JavaScript, and Python normalizes the preserved field to a target-local quote character; downstream string and import rendering no longer read the shared enumCompatibility-held user preference whose concrete grammar belongs to each languageLanguage-local quote handling owns escaping and conveniences; the old enum, field, and setter remain deprecated shims

Built-in unit tests that directly inspect config-return values are temporary migration expectations, not additional production readers. tests/renderer_parity_tests.rs protects the exact built-in renderer-event matrix, direct/pretty parity, and legacy indentation compatibility; the field/property custom-adapter tests exercise compatibility defaults; and tests/assert_quote_tests.rs plus the three language unit suites protect the quote shim. Definitions and overrides under src/lang/*.rs remain until the corresponding compatibility surface can be removed in a future major version.

Legacy Surface Matrix

FamilyLegacy surfaceCompatibility behaviorCurrent replacement
CapabilitiesNo capabilities() overrideExternal adapters receive LanguageCapabilities::permissive()Return a strict matrix with exact family profiles
Type expressionstype_presentation(), TypePresentationConfig, TypePresentation, FunctionPresentation, generic_syntax(), GenericSyntaxConfig, qualified-name presentation accessors, and TypeName::to_doc_with_lang()The provided lower_type_name() reproduces 0.6.8 output for old TypeName variants and rejects StringLiteral or any later variant; the direct document method remains only as a deprecated terminal facadeImplement complete fallible RendererLang::lower_type_name() and keep imports in the returned CodeBlock
TypeName matching and documented JSON valuesExhaustive matches over the pre-0.6.8 variants; concrete TypeName JSON values documented before 0.7Supported Rust constructors remain; checked fixtures preserve the documented JSON values. Generic Serde support does not promise compatibility for other representations, binary encodings, enum ordinals, field order, or serializer bytesAdd a wildcard arm to downstream matches; do not reinterpret unknown data or rely on an undocumented wire format
Functionsfunction_keyword(), fun_block_open(), function_syntax(), FunctionSyntaxConfig, ParamListStyle, FunctionSignatureStyle, ConstructorDelegationStyle, and WhereClauseStyleThe provided lower_function() interprets them for external adaptersvalidate_function() and complete lower_function()
Typestype_keyword(), methods_inside_type_body(), type_kind_suffix(), emit_newtype_decl(), type_header_block_open(), type_body_prefix() / type_body_suffix(), emit_type_close_suffix(), abstract_type_modifier_is_valid(), type_decl_syntax(), and type-emitter reads of function_syntax() / enum_and_annotation()The provided lower_type() interprets them only for permissive external adapters and does not infer later closed-sum intentvalidate_type(), complete lower_type(), and the dedicated closed-sum builder
Type parametersgeneric_syntax(), render_type_params(), render_type_param_kind(), and ParameterSpec::emit_into()The provided permissive declaration lowerers and direct facades preserve frozen 0.6.8 grammarComplete language-owned type and function lowering; strict adapters without a complete function lowerer fail with MissingFunctionLowerer
Type application inputsTypeName::Generic, TypeName::generic()Explicitly deprecated; old storage and checked JSON fixtures remain supportedTypeName::Application / application() with ordered TypeArgument values
Callable type inputsTypeName::Function, TypeName::function()Explicitly deprecated; existing scalar-slot meaning remains supportedTypeName::Callable / callable() with CallableParam values
Declaration binding inputsTypeParamSpec, TypeParamKind, and FunSpecBuilder::add_type_param() / TypeSpecBuilder::add_type_param()Explicitly deprecated; released bounds, context bounds, lifetime intent, and raw Scala suffix metadata remain compatibility inputsFallible GenericParamSpec, GenericParamDomain, KindExpr, and add_generic_param()
Variable spellingvariable_prefix()Frozen function, field, property, and type compatibility lowerers interpret the adapter’s prefixComplete language-owned declaration lowering
Preamblesdoc_before_annotations(), doc_comment_inside_body()Frozen compatibility lowerers may read themEmit documentation and attributes in each complete lowerer
Fieldsoptional_field_style(), OptionalFieldStyleThe provided lower_fields() freezes the old field emitterFieldCapability, FieldContext, TypeName::Optional, and complete lower_fields()
Propertiesproperty_style(), property_getter_keyword(), PropertyStyleThe provided lower_property() freezes the old property emitterPropertyContext, property capabilities, and complete lower_property()
VariantsVariantContext, .value(), VariantValueFormat, variants_before_fieldsOnly permissive external adapters retain ownerless positional lowering; strict built-ins require an owner and complete sequenceAdd variants to TypeSpec; use .discriminant() or .constructor_argument()
Variant payload builders.associated_type(), .add_field()Deprecated aliases remain available.positional_payload(), .record_payload_field()
Renderer events and block nodesblock_syntax(), BlockSyntaxConfig, block_open_for(), block_close_for(), intent-aware bridge hooks, and legacy string-only block nodesProvided event defaults interpret old config and hooks; old nodes remain source-constructible and renderable, unchanged external adapters remain compatible, and no versioned Serde representation is promisedBlockIntent, indent_unit(), render_statement_end(), render_block_open(), render_block_close(), and render_branch_transition()

Direct FieldSpec::emit() and PropertySpec::emit() remain public facades. Their DeclarationContext input is retained only as a compatibility payload. Prefer adding members to TypeSpec whenever the owning TypeKind or other members can affect validity.

Structured Parametric Inputs

New bindings are constructed fallibly; existing released constructors keep their signatures. Owners retain one ordered binding sequence, including mixed old and new inputs, and expose it through borrowed generic_params() views. Modern kind annotations are never reconstructed from legacy raw suffixes. GenericParamView::legacy_kind() is explicitly deprecated and exists only for that retained compatibility metadata.

Ordinary legacy application arguments migrate to TypeArgument::Single. Ordinary legacy callable slots migrate to unnamed required CallableParam::Single values. Expansion patterns, optional presence, and repeated-element segments are new explicit intent; no old vector is reinterpreted as a pack. A frozen compatibility adapter rejects modern application/callable values or binding domains it cannot preserve.

The new expression enums and binding domains are non-exhaustive for downstream matching. This adds no unknown-node or cross-version serialization contract. Unreleased owner-view APIs and the unreleased closed-sum builder are not classified as released legacy surfaces.

Frozen Grammar Configuration

The legacy structs mix renderer mechanics with type-expression and declaration grammar. Built-in type-name and declaration lowering no longer read type_presentation() or generic_syntax(); only the frozen external-adapter bridges and direct compatibility facades do. Final renderer paths no longer read block_syntax(). Complete language-local lowerers own type and declaration grammar, while direct renderer-event methods plus indent_unit() own final rendering. Frozen compatibility defaults and lowerers may continue interpreting the old values; none of these structs receives new fields or variants.

TypePresentationConfig and GenericSyntaxConfig

These values describe the pre-0.6.8 shared type-expression and declaration grammar: generic delimiters, bounds, prefix and postfix wrappers, infix separators, qualified-name separators, and function-type placement. The provided RendererLang::lower_type_name(), permissive declaration lowerers, and deprecated direct facades continue to interpret the applicable fields so an existing external adapter remains source compatible.

These bridges are intentionally closed. Type-name compatibility rejects TypeName::StringLiteral and every later semantic variant, even if one of the old presentation patterns could produce plausible text. New and built-in adapters implement complete fallible type-name and declaration lowering instead of extending the configuration.

FunctionSyntaxConfig

Field0.6.8 meaning
return_type_separatorText between a parameter list and suffix return type
async_keyword, async_suffix, async_suffix_before_returnAsync spelling and placement
abstract_keywordAbstract/virtual spelling
param_list_styleTupled or curried parameter layout
function_signature_styleMerged or split declaration layout
constructor_keyword, constructor_delegation_styleConstructor spelling and delegation placement
where_clause_styleInline, block, or repeated where-clause placement
empty_bodyLegacy body placeholder
type_params_before_return_typeLegacy type-parameter placement switch

Complete function lowerers own all of these choices locally. An adapter may share private policy-free helpers, but new syntax must not add another field to this table.

TypeDeclSyntaxConfig

FieldFrozen compatibility meaning
type_before_name, return_type_is_prefix, type_annotation_separatorType/name ordering used by compatibility lowerers
super_type_keyword, super_type_separator, super_type_subsequent_separatorBase-type grammar
implements_keywordImplemented-interface grammar
type_alias_target_firstAlias target/name ordering
supports_primary_constructorLegacy primary-constructor switch

These fields may be read only by frozen compatibility lowerers. New adapters implement complete declaration lowering instead.

EnumAndAnnotationConfig

FieldTransitional or compatibility meaning
variant_prefix, variant_prefix_first, variant_separator, variant_trailing_separator, variants_before_fields, variant_value_formatFrozen external-adapter variant grammar
annotation_prefix, annotation_suffixLegacy annotation spelling; complete lowerers use local structured emission
readonly_keyword, mutable_field_keywordFrozen parameter/property-promotion fragments

Quote-style compatibility

QuoteStyle, the public quote_style fields, and with_quote_style(QuoteStyle) predate 0.6.8 and remain source-compatible. They are deprecated shims rather than a general quote configuration shared by new languages. TypeScript, JavaScript, and Python each own quote normalization, escaping, and output locally. Their with_single_quotes() and with_double_quotes() conveniences update the preserved field so there is one stored choice and no precedence rule.

Import resolver compatibility

ImportGroup::resolve() and resolve_with_explicit() remain the exact deprecated, infallible 0.6.8 algorithms. They preserve first-encountered and explicit-entry precedence, including cases that can produce duplicate local bindings. ImportGroup::try_resolve() and try_resolve_with() are the current fallible complete-set entry points; the old methods are not implemented by unwrapping the new resolver.

Builder Migration Recipes

Type names and exhaustive matches

TypeName gains StringLiteral(String) in 0.7 and is marked #[non_exhaustive]. Downstream code that previously matched every variant must add a wildcard arm and decide whether an unknown type should be rejected or passed back to sigil-stitch for language-owned lowering. Do not widen an unknown variant to Primitive or Raw.

The string-literal payload is the decoded string value. Several values compose as TypeName::Union; do not preserve hand-written quotes in Raw when the semantic singleton form is available. Exact fixtures cover the TypeName JSON values documented before 0.7. No other Serde representation or binary format receives a cross-version guarantee. Deserializing an unknown variant remains an error; it is never reinterpreted as another type.

Closed sums

Use the dedicated closed-sum builder for a complete set of unit, positional, or record cases. Do not encode this intent as TypeKind::Enum plus a sealed flag, discriminants, or constructor arguments. The 0.6.8 TypeKind enum stays unchanged; ordinary enum construction and matching remain source-compatible.

A zero-case closed sum is a named uninhabited declaration. A target may use a canonical empty type only when that representation preserves the declaration’s name and valid use positions exactly. The shared model does not add a Never type reference or equate declaration intent with bottom-subtype semantics in this feature. Targets without an exact named empty-sum declaration reject that shape.

Closed-sum intent is new in 0.7 and has no mixed-version interpretation. Producers, consumers of newly serialized specs, and external adapters using the new semantic views must upgrade together. This requirement does not create a general cross-version Serde or binary-format contract.

Enum variants

  • Replace direct EnumVariantSpec::emit(..., VariantContext) with TypeSpec::add_variant() so the adapter receives the owner and full sequence.
  • Replace .value(x) with .discriminant(x) when the value identifies the member, or .constructor_argument(x) when the enum entry invokes a constructor.
  • Replace .associated_type(t) with .positional_payload(t) and .add_field(f) with .record_payload_field(f).

Strict built-ins reject an ownerless variant when first/last flags cannot prove valid separators, payload grammar, or section termination.

Optional fields

FieldSpec::is_optional() means that the containing value may omit the field. TypeName::Optional(T) means that a present field can carry an absent or null value. Replace OptionalFieldStyle with the semantic form actually intended; do not infer one meaning from the other.

Computed properties

Add a PropertySpec to TypeSpec instead of relying on direct placement when the target’s owning type or member namespaces affect validity. New adapters lower read and write behavior from PropertyIntent; they do not select an accessor model through PropertyStyle.

Primary constructor parameters

Pass only an identifier to ParameterSpec. For Kotlin and Scala, use .is_property() for an immutable promoted property and .is_mutable_property() for a mutable one. Do not encode val or var in the parameter name. Complete language lowerers own that spelling; unsupported languages reject primary-constructor intent instead of ignoring it.

Haskell and OCaml constructor data is not a primary constructor. Model it with enum-variant positional or record payloads so the algebraic-data adapter sees the payload semantics directly.

C++ static member initializers

For class and struct members, the C++ adapter preserves the pre-C++17 static const spelling only when FieldSpec proves an integral primitive type. It rejects:

  • initialized mutable static members, which require either a C++17 inline declaration or a separate out-of-class definition; and
  • initialized read-only static members whose type is not provably integral.

TypeName does not currently distinguish an enum type from another named type, so the adapter does not guess from capitalization or a raw type name. Use TypeSpecBuilder::extra_member(CodeBlock) for an enum-typed class constant, or materialize the declaration and out-of-class definition as target-specific blocks. This restriction prevents a compatibility path from silently emitting invalid C++.

External Adapter Migration

Migrate one declaration family at a time:

  1. Implement complete lower_type_name() handling for every accepted old variant and explicit errors for unsupported forms.
  2. Add a strict profile for every supported semantic context or owner kind.
  3. Add adapter-local validation for identifier rules, modifier combinations, and relationships the profile cannot express.
  4. Implement the complete lower_* seam and preserve every accepted TypeName as a %T reference and every nested block as structured %L.
  5. Cover direct and owner-aware success and failure paths, import aliases, and both direct and pretty renderer paths where soft breaks are reachable.
  6. Remove migrated-family reads of deprecated grammar from the adapter.

Keep rendered-output fixtures while migrating. A provided default is a compatibility bridge, not evidence that an adapter has completed the new seam.

Python Static-Decorator Compatibility

Python retains one adapter-local compatibility recognizer for the 0.6.8 pattern that combines FunSpec::is_static() with a staticmethod or classmethod decorator. It applies only to non-constructor member and interface-member functions.

The recognizer accepts:

  • AnnotationSpec::new("staticmethod") or AnnotationSpec::new("classmethod"), including an importable annotation with that simple name; or
  • an opaque annotation block made only of literal/nested-literal nodes whose trimmed text is exactly @staticmethod or @classmethod (and the equivalent attribute node).

Other spellings do not acquire static-method semantics. New code should prefer the structured AnnotationSpec form. This exception is Python-local and must not become a shared decorator parser or syntax hook.

Compatibility Testing

Run the focused compatibility gates with:

cargo test --test compatibility_0_6_8
just semver-check

The first command compiles the old adapter as an external crate, checks the restored signatures and structural marker bridges, and compares the bounded JSON fixtures. The second command tests the report parser and then compares the complete cargo-semver-checks 0.50.0 record set with the checked allowlist. Missing, duplicate, malformed, and unexpected approved records fail closed.

For a migrated family, keep tests for:

  • an adapter implementing only the 0.6.8 trait surface;
  • valid legacy output preserved by the provided lowerer;
  • StringLiteral rejected by an adapter that implements only the 0.6.8 type presentation surface;
  • invalid or ownerless built-in input rejected before materialization;
  • direct and FileSpec paths selecting the actual adapter;
  • semantic replacements for every deprecated builder alias; and
  • serialized legacy nodes or fields that remain part of the public contract.

What Does Not Belong Here

This appendix is not a release history, exhaustive API reference, or rejected- design catalogue. Release-by-release changes belong in CHANGELOG.md, exact signatures and deprecation attributes belong in rustdoc, and durable design rationale belongs in focused records under docs/adr/.