Eliminate need for forward type declarations in recursive datatypes#443
Merged
joehendrix merged 10 commits intomainfrom Mar 5, 2026
Merged
Eliminate need for forward type declarations in recursive datatypes#443joehendrix merged 10 commits intomainfrom
joehendrix merged 10 commits intomainfrom
Conversation
4971e76 to
64accb0
Compare
ab52636 to
ad51d1d
Compare
MikaelMayer
reviewed
Feb 26, 2026
Contributor
MikaelMayer
left a comment
There was a problem hiding this comment.
Clean, well-motivated PR that removes a real source of user friction. The two-phase elaboration design in elaborateWithPreRegistration is sound, the GlobalContext simplification is a clear win, and the removal of DeclState/forward declarations is thorough — no dangling references remain.
One minor inconsistency between extractParamNames and the inline code in runSyntaxElaborator (.tvar handling) is worth a look. The rest is solid.
I'd approve after the one question below is addressed.
5347405 to
b334bd5
Compare
MikaelMayer
approved these changes
Mar 5, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass `gctx` directly through the template expansion monad instead of building an intermediate `Std.HashSet String` of existing names and folding `ensureDefined` over results afterward. This removes `TemplateExpansionResult` and the two unnecessary folds in `addDatatypeBindings`, using `GlobalContext` membership for duplicate detection and `ensureDefined` as functions are generated. The template expansion machinery is moved after the `GlobalContext` definition to resolve forward-reference issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change extractFieldsFromBindings, extractSingleConstructor, and extractConstructorInfo to return Except String instead of silently returning empty arrays/none on errors. This surfaces clear error messages for bad arg shapes, missing annotations, and out-of-bounds indices. Update callers: - addDatatypeBindings! (renamed with !) uses panic! on error - collectConstructorNames returns #[] on error (non-fatal for pre-registration) - translateConstructorList forwards errors via TransM.error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GlobalContext.defineChecked with preRegistered flag for explicit membership/freshness checking at define sites - Replace ensureDefined in addDatatypeBindings!, addCommand.addBinding, and preRegisterType with defineChecked or direct define-with-proof - Add atomic buildAndDefine for template function expansion (no TOCTOU) - Thread preRegistered : Bool through addCommand → addBinding - Extract elabSyntaxArg and resultContext from runSyntaxElaborator - Add docstrings to preRegisterType, runSyntaxElaborator, extractDatatypeInfo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…review items - Move constructor info extraction functions out of GlobalContext namespace (they only depend on DialectMap, not GlobalContext) - Move GlobalContext definition after constructor info extraction - Lift addBinding from where clause to standalone private def - Move collect helper and checkConstructorUniqueness to Elab.lean - Add elaboration-time constructor uniqueness check in evalBindingSpec - Remove callerInfo parameter and FIXME debug diagnostic from runSyntaxElaborator - Fix typos (datype, assignemnt) and stale function name references - Wrap long lines to stay within 100 char limit - Make OperationF.foldBindingSpecs public Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rUniqueness Calls expandFunctionTemplates in evalBindingSpec's .datatype branch so name collisions (duplicate constructors, duplicate template functions) are reported via logError during elaboration rather than panicking in addDatatypeBindings!. Removes checkConstructorUniqueness from Elab.lean since constructor names no longer need to be globally unique — collisions are now caught by the template expansion check which covers both constructor and generated function names. Adds function templates (perConstructor tester, perField accessor) to the DDM MutualDatatypes test dialect and adds Test 5 for complex mutual types with multiple constructors and fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds runChecked to TemplateExpandM to track which constructors failed registration. Skips template expansion for failed constructors so we report only the root cause (duplicate constructor name) rather than cascading into duplicate template function errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add @[prec(30), leftassoc] to constructorListPush so constructor arguments (at maxPrec) don't get unnecessarily parenthesized. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b334bd5 to
9bcc0d7
Compare
joscoh
approved these changes
Mar 5, 2026
MikaelMayer
approved these changes
Mar 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Mutually recursive datatypes previously required users to manually write
forward typedeclarations before amutualblock. This PR eliminatesthat boilerplate by having the elaborator automatically discover and
pre-register type names before elaborating the block's children.
Key changes
@[preRegisterTypes(scope)]annotation oncommand_mutualtriggerstwo-phase elaboration: Phase 1 partially elaborates each child to
extract type names and params, then pre-registers them in the
GlobalContext; Phase 2 elaborates the full block with mutualreferences already resolved.
GlobalContextsimplified. TheDeclStateenum(
.forward/.defined) and its bookkeeping are removed.definenow requires a non-membership proof, making illegal states
unrepresentable.
defineCheckedtakes apreRegisteredflag tohandle both fresh definitions and pre-registered confirmations.
extractConstructorInfoand helpers returnExcept Stringinsteadof silently returning empty arrays. Template expansion uses an atomic
buildAndDefinethat checks freshness and defines in one step.evalBindingSpec's.datatypebranch callsexpandFunctionTemplatesto catch constructor and template function name collisions early via
logError, rather than panicking inaddCommand. Constructor namesare no longer required to be globally unique — only the generated
template function names must be distinct.
elabSyntaxArg,resultContext,extractParamNames, andscopeSepFormatfrom the monolithicrunSyntaxElaborator. LiftedextractConstructorInfoout of theGlobalContextnamespace (it only depends onDialectMap).argElabIndex. A precomputedArray (Option Nat)maps eachargLevelto its position inargElaborators, replacing linear searches.translateMutualBlockallocatesplaceholder entries on the fly when a datatype isn't already
pre-registered, rather than requiring forward declarations.
Test changes
StrataTest/DDM/MutualDatatypes.lean(new): DDM-level testdialect with function templates (
perConstructortester,perFieldaccessor). Tests mutual block visibility, three-wayrecursion, empty blocks, multi-constructor template expansion, and
negative cases (undefined type, duplicate type, type clash, duplicate
constructor name collisions, reused constructor name collisions).
StrataTest/Languages/Core/Examples/MutualDatatypes.lean(new):Core-level integration tests verifying tester functions, accessor
functions, equality, polymorphic havoc, and complex Stmt/StmtList
patterns on mutually recursive types.
StrataTest/Languages/Core/TestASTtoCST.lean(new):Round-trip tests for AST-to-CST translation.
forward typedeclarations from existing test files.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.